1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 03:28:20 +02:00
Beef/IDE/src/IDEApp.bf

15472 lines
413 KiB
Beef
Raw Normal View History

2019-09-29 07:44:23 -07:00
using System;
using System.Security.Cryptography;
using System.Text;
using System.Collections;
2019-08-23 11:56:54 -07:00
using System.Text;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Beefy;
using Beefy.widgets;
using Beefy.gfx;
using Beefy.theme;
using Beefy.theme.dark;
using Beefy.sys;
using Beefy.events;
using Beefy.geom;
using Beefy.res;
using System.Diagnostics;
using Beefy.utils;
using IDE.Debugger;
using IDE.Compiler;
using IDE.Util;
2019-09-29 07:44:23 -07:00
using IDE.ui;
2019-08-23 11:56:54 -07:00
using IDE.util;
2024-08-27 05:48:22 +02:00
[AttributeUsage(.Method, .ReflectAttribute | .AlwaysIncludeTarget, ReflectUser = .All)]
2019-08-23 11:56:54 -07:00
struct IDECommandAttribute : Attribute
{
}
namespace IDE
{
2024-08-27 05:48:22 +02:00
public class WindowData
{
public DrawLayer mOverlayLayer ~ delete _;
}
2019-08-23 11:56:54 -07:00
enum SourceShowType
{
ShowExisting,
ShowExistingInActivePanel,
New,
2024-08-27 05:48:22 +02:00
Temp,
2019-08-23 11:56:54 -07:00
}
enum Verbosity
{
Default,
2019-08-23 11:56:54 -07:00
Quiet,
Minimal,
Normal,
Detailed,
Diagnostic
2019-08-23 11:56:54 -07:00
}
class DeferredUserRequest
{
}
class DeferredShowPCLocation : DeferredUserRequest
{
public int32 mStackIdx;
public this(int32 stackIdx)
{
mStackIdx = stackIdx;
}
}
enum BeefVerb
{
None,
Open,
New,
OpenOrNew,
Test,
Run,
2024-10-21 09:18:07 -04:00
Update,
2024-10-25 17:20:00 -04:00
GetVersion,
CleanCache
2019-08-23 11:56:54 -07:00
}
enum HotResolveState
{
None,
Pending,
PendingWithDataChanges
}
public class IDETabbedView : DarkTabbedView
{
2020-05-20 06:44:21 -07:00
public this(SharedData sharedData) : base(sharedData)
{
if (sharedData == null)
{
mSharedData.mOpenNewWindowDelegate.Add(new (fromTabbedView, newWindow) => gApp.SetupNewWindow(newWindow, true));
mSharedData.mTabbedViewClosed.Add(new (tabbedView) =>
2024-08-27 05:48:22 +02:00
{
2020-05-20 06:44:21 -07:00
if (tabbedView == gApp.mActiveDocumentsTabbedView)
gApp.mActiveDocumentsTabbedView = null;
2024-08-27 05:48:22 +02:00
});
2020-05-20 06:44:21 -07:00
}
}
2019-08-23 11:56:54 -07:00
public ~this()
{
if (gApp.mActiveDocumentsTabbedView == this)
gApp.mActiveDocumentsTabbedView = null;
}
2020-05-20 06:44:21 -07:00
public override TabbedView CreateTabbedView(SharedData sharedData)
{
IDETabbedView tabbedView = new IDETabbedView(sharedData);
return tabbedView;
}
2019-08-23 11:56:54 -07:00
}
2024-08-27 05:48:22 +02:00
public class IDEApp : BFApp
{
2019-08-23 11:56:54 -07:00
public static String sRTVersionStr = "042";
2025-03-19 11:01:28 -04:00
public const String cVersion = "0.43.6";
2019-08-23 11:56:54 -07:00
#if BF_PLATFORM_WINDOWS
public static readonly String sPlatform64Name = "Win64";
public static readonly String sPlatform32Name = "Win32";
2024-08-27 05:48:22 +02:00
#elif BF_PLATFORM_LINUX
2019-08-23 11:56:54 -07:00
public static readonly String sPlatform64Name = "Linux64";
public static readonly String sPlatform32Name = "Linux32";
2024-08-27 05:48:22 +02:00
#elif BF_PLATFORM_MACOS
public static readonly String sPlatform64Name = "macOS";
public static readonly String sPlatform32Name = null;
2019-08-23 11:56:54 -07:00
#else
public static readonly String sPlatform64Name = "Unknown64";
public static readonly String sPlatform32Name = "Unknown32";
#endif
public static bool sExitTest;
public Verbosity mVerbosity = .Default;
2019-08-23 11:56:54 -07:00
public BeefVerb mVerb;
public bool mDbgCompileDump;
public int mDbgCompileIdx = -1;
public String mDbgCompileDir ~ delete _;
public String mDbgVersionedCompileDir ~ delete _;
public DateTime mDbgHighestTime;
2019-10-05 10:24:58 -07:00
public bool mForceFirstRun;
2019-09-16 09:32:02 -07:00
public bool mIsFirstRun;
2020-10-15 10:44:29 -07:00
public bool mSafeMode;
public String mDeferredRelaunchCmd ~ delete _;
public int? mTargetExitCode;
2019-09-18 08:13:00 -07:00
public FileVersionInfo mVersionInfo ~ delete _;
2019-08-23 11:56:54 -07:00
//public ToolboxPanel mToolboxPanel;
public Monitor mMonitor = new Monitor() ~ delete _;
2024-08-27 05:48:22 +02:00
public Thread mMainThread;
public PropertiesPanel mPropertiesPanel;
public Font mTinyCodeFont ~ delete _;
public Font mCodeFont ~ delete _;
2024-07-19 10:31:33 +02:00
public Font mTermFont ~ delete _;
2019-08-23 11:56:54 -07:00
protected bool mInitialized;
public bool mConfig_NoIR;
public bool mFailed;
public bool mLastTestFailed;
public bool mLastCompileFailed;
public bool mLastCompileSucceeded;
public bool mLastCompileHadMessages;
public bool mPauseOnExit;
public bool mDbgDelayedAutocomplete;
public bool mDbgTimeAutocomplete;
public bool mDbgPerfAutocomplete;
2019-08-23 11:56:54 -07:00
public BeefConfig mBeefConfig = new BeefConfig() ~ delete _;
public List<String> mDeferredFails = new .() ~ DeleteContainerAndItems!(_);
public String mInitialCWD = new .() ~ delete _;
public Commands mCommands = new .() ~ delete _;
public KeyChordState mKeyChordState ~ delete _;
2024-08-27 05:48:22 +02:00
public WidgetWindow mMainWindow;
public DarkDockingFrame mDockingFrame;
public MainFrame mMainFrame;
public GlobalUndoManager mGlobalUndoManager = new GlobalUndoManager() ~ delete _;
2019-08-23 11:56:54 -07:00
public SourceControl mSourceControl = new SourceControl() ~ delete _;
2024-10-21 09:18:07 -04:00
public GitManager mGitManager = new .() ~ delete _;
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public WidgetWindow mPopupWindow;
public RecentFileSelector mRecentFileSelector;
public IDETabbedView mActiveDocumentsTabbedView;
public static new IDEApp sApp;
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public Image mTransparencyGridImage ~ delete _;
public Image mSquiggleImage ~ delete _;
public Image mCircleImage ~ delete _;
public bool mWantShowOutput;
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public OutputPanel mOutputPanel;
#if BF_PLATFORM_WINDOWS
2024-07-19 10:31:33 +02:00
public TerminalPanel mTerminalPanel;
public ConsolePanel mConsolePanel;
2024-08-27 05:48:22 +02:00
#endif
public ImmediatePanel mImmediatePanel;
public FindResultsPanel mFindResultsPanel;
public WatchPanel mAutoWatchPanel;
public WatchPanel mWatchPanel;
public MemoryPanel mMemoryPanel;
public CallStackPanel mCallStackPanel;
public ErrorsPanel mErrorsPanel;
2024-08-27 05:48:22 +02:00
public BreakpointPanel mBreakpointPanel;
2020-07-18 06:50:28 -07:00
public DiagnosticsPanel mDiagnosticsPanel;
2019-08-23 11:56:54 -07:00
public ModulePanel mModulePanel;
2024-08-27 05:48:22 +02:00
public ThreadPanel mThreadPanel;
2019-08-23 11:56:54 -07:00
public ProfilePanel mProfilePanel;
2024-08-27 05:48:22 +02:00
public ProjectPanel mProjectPanel;
2019-08-23 11:56:54 -07:00
public ClassViewPanel mClassViewPanel;
public Widget mLastActivePanel;
public SourceViewPanel mLastActiveSourceViewPanel;
public AutoCompletePanel mAutoCompletePanel;
2022-06-08 20:11:39 +02:00
public BookmarksPanel mBookmarksPanel;
2024-08-27 05:48:22 +02:00
public Rect mRequestedWindowRect = Rect(64, 64, 1200, 1024);
2019-09-27 13:03:47 -07:00
public BFWindow.ShowKind mRequestedShowKind;
2024-08-27 05:48:22 +02:00
public WakaTime mWakaTime ~ delete _;
2019-08-23 11:56:54 -07:00
2021-02-25 08:10:21 -08:00
public PackMan mPackMan = new PackMan() ~ delete _;
2024-10-21 09:18:07 -04:00
public HashSet<String> mWantUpdateVersionLocks ~ DeleteContainerAndItems!(_);
2019-08-23 11:56:54 -07:00
public Settings mSettings = new Settings() ~ delete _;
2024-08-27 05:48:22 +02:00
public Workspace mWorkspace = new Workspace() ~ delete _;
public FileWatcher mFileWatcher = new FileWatcher() ~ delete _;
2020-09-18 05:30:21 -07:00
#if !CLI
public FileRecovery mFileRecovery = new FileRecovery() ~ delete _;
#endif
2019-08-23 11:56:54 -07:00
public int mLastFileChangeId;
2024-08-27 05:48:22 +02:00
public bool mHaveSourcesChangedInternallySinceLastCompile;
public bool mHaveSourcesChangedExternallySinceLastCompile;
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public String mConfigName = new String() ~ delete _;
public String mPlatformName = new String() ~ delete _;
2019-08-23 11:56:54 -07:00
public String mSetConfigName ~ delete _;
public String mSetPlatformName ~ delete _;
2024-08-27 05:48:22 +02:00
public Targets mTargets = new Targets() ~ delete _;
public DebugManager mDebugger ~ delete _;
2019-08-23 11:56:54 -07:00
public String mSymSrvStatus = new String() ~ delete _;
2020-05-08 07:10:35 -07:00
public delegate void(String) mPendingDebugExprHandler = null;
2019-08-23 11:56:54 -07:00
public bool mIsImmediateDebugExprEval;
2024-08-27 05:48:22 +02:00
public BookmarkManager mBookmarkManager = new BookmarkManager() ~ delete _;
public HistoryManager mHistoryManager = new HistoryManager() ~ delete _;
public Stopwatch mCompileAndRunStopwatch ~ delete _;
2019-08-23 11:56:54 -07:00
// The Beef build system only gets populated when a build is requested,
// but the Clang build system keeps up-to-date with the projects' files
// and watches for file changes
2024-08-27 05:48:22 +02:00
public BfSystem mBfBuildSystem ~ delete _;
public BfCompiler mBfBuildCompiler;
2019-08-23 11:56:54 -07:00
public int mCompileSinceCleanCount;
public BuildContext mBuildContext ~ delete _;
2019-08-23 11:56:54 -07:00
#if IDE_C_SUPPORT
2024-08-27 05:48:22 +02:00
public ClangCompiler mDepClang ~ delete _;
2019-08-23 11:56:54 -07:00
#endif
2025-03-19 11:01:28 -04:00
// The Beef resolve system is up-to-date with the projects' files,
2019-08-23 11:56:54 -07:00
// but the Clang resolver only has open files in it
public bool mNoResolve = false;
public bool mDeterministic = false;
2024-08-27 05:48:22 +02:00
public BfSystem mBfResolveSystem ~ delete _;
public BfCompiler mBfResolveCompiler;
public BfResolveHelper mBfResolveHelper ~ delete _;
2019-08-23 11:56:54 -07:00
#if IDE_C_SUPPORT
2024-08-27 05:48:22 +02:00
public ClangCompiler mResolveClang;
2019-08-23 11:56:54 -07:00
#endif
2024-08-27 05:48:22 +02:00
public SpellChecker mSpellChecker;
2019-08-23 11:56:54 -07:00
public List<String> mExternalChangeDeferredOpen = new List<String>() ~ DeleteContainerAndItems!(_);
public SettingHistoryManager mLaunchHistoryManager = new SettingHistoryManager(true) ~ delete _;
public SettingHistoryManager mFindAndReplaceHistoryManager = new SettingHistoryManager(true) ~ delete _;
List<Object> mIdleDeferredDeletes = new List<Object>() ~ delete _;
public bool mCompilingBeef = false;
2024-08-27 05:48:22 +02:00
public bool mWantsClean = false;
public bool mWantsBeefClean = false;
2019-08-23 11:56:54 -07:00
public bool mWantsRehupCallstack = false;
2024-08-27 05:48:22 +02:00
public bool mDebugAutoBuild = false;
public bool mDebugAutoRun = false;
2019-08-23 11:56:54 -07:00
public bool mDisableBuilding;
public int32 mDebugAutoShutdownCounter;
2024-08-27 05:48:22 +02:00
public bool mTargetDidInitBreak = false;
public bool mTargetHadFirstBreak = false;
public bool mTargetStartWithStep = false;
public Breakpoint mMainBreakpoint;
public Breakpoint mMainBreakpoint2;
2024-08-27 05:48:22 +02:00
public bool mInDisassemblyView;
2019-08-23 11:56:54 -07:00
public bool mIsAttachPendingSourceShow;
public bool mStepOverExternalFiles;
public bool mRunningTestScript;
public bool mStartedWithTestScript;
2019-08-23 11:56:54 -07:00
public bool mExitWhenTestScriptDone = true;
public ScriptManager mScriptManager = new ScriptManager() ~ delete _;
2020-02-21 06:52:08 -08:00
public TestManager mTestManager ~ delete _;
2024-08-27 05:48:22 +02:00
public bool mExecutionPaused = false;
2019-08-23 11:56:54 -07:00
public HotResolveState mHotResolveState;
public int mHotResolveTryIdx;
public bool mDebuggerPerformingTask = false; // Executing an expression, loading from symbol server
2024-08-27 05:48:22 +02:00
public int32 mForegroundTargetCountdown = 0;
2019-08-23 11:56:54 -07:00
public int32 mDebuggerContinueIdx;
2024-08-27 05:48:22 +02:00
public SysMenu mWindowMenu;
2019-08-23 11:56:54 -07:00
public List<SourceViewPanel> mPostRemoveUpdatePanels = new .() ~ delete _;
public List<String> mRecentlyDisplayedFiles = new List<String>() ~ DeleteContainerAndItems!(_);
2024-08-27 05:48:22 +02:00
public List<SysMenu> mRecentlyDisplayedFilesMenuItems = new List<SysMenu>() ~ delete _;
public Dictionary<BFWindow, WindowData> mWindowDatas = new Dictionary<BFWindow, WindowData>() ~ delete _;
2019-08-23 11:56:54 -07:00
public Dictionary<String, FileEditData> mFileEditData = new Dictionary<String, FileEditData>() ~
2024-08-27 05:48:22 +02:00
{
2019-08-23 11:56:54 -07:00
for (var editDataPair in mFileEditData)
{
delete editDataPair.key;
editDataPair.value.Deref();
}
delete _;
2024-08-27 05:48:22 +02:00
};
2019-08-23 11:56:54 -07:00
public int32 mFileDataDataRevision;
2024-08-27 05:48:22 +02:00
2025-03-19 11:01:28 -04:00
/*public Point mLastAbsMousePos;
2019-08-23 11:56:54 -07:00
public Point mLastRelMousePos;
2024-08-27 05:48:22 +02:00
public int32 mMouseStillTicks;
public Widget mLastMouseWidget;*/
public bool mAppHasFocus;
2024-08-27 05:48:22 +02:00
public int32 mCloseDelay;
public FileChangedDialog mFileChangedDialog;
public FindAndReplaceDialog mFindAndReplaceDialog;
2019-08-23 11:56:54 -07:00
public LaunchDialog mLaunchDialog;
2024-08-27 05:48:22 +02:00
public SymbolReferenceHelper mSymbolReferenceHelper;
2019-08-23 11:56:54 -07:00
public WrappedMenuValue mViewWhiteSpace = .(false);
public bool mEnableGCCollect = true;
public bool mDbgFastUpdate;
public bool mTestEnableConsole = false;
public bool mTestBreakOnFailure = true;
2019-08-23 11:56:54 -07:00
public ProfileInstance mLongUpdateProfileId;
public uint32 mLastLongUpdateCheck;
public uint32 mLastLongUpdateCheckError;
bool mRunTest;
int mRunTestDelay;
bool mEnableRunTiming = false; ///
ProfileInstance mRunTimingProfileId;
bool mDoingRunTiming;
bool mProfileCompile = false;
ProfileInstance mProfileCompileProfileId;
2024-07-23 07:56:23 +02:00
Monitor mDebugOutputMonitor = new .() ~ delete _;
String mDebugOutput = new .() ~ delete _;
2019-08-23 11:56:54 -07:00
#if !CLI
public IPCHelper mIPCHelper ~ delete _;
public bool mIPCHadFocus;
#endif
int32 mProcessAttachId;
#if BF_PLATFORM_WINDOWS
Windows.EventHandle mProcessAttachHandle;
#endif
String mCrashDumpPath ~ delete _;
bool mShowedFirstDocument = false;
class LaunchData
{
public String mTargetPath ~ delete _;
2019-08-23 11:56:54 -07:00
public String mArgs ~ delete _;
public String mWorkingDir ~ delete _;
public bool mPaused = false;
}
LaunchData mLaunchData ~ delete _;
DeferredUserRequest mDeferredUserRequest ~ delete _;
enum DeferredOpenKind
{
None,
File,
Workspace,
NewWorkspace,
NewWorkspaceOrProject,
2019-08-23 11:56:54 -07:00
CrashDump,
DebugSession
}
2024-08-27 05:48:22 +02:00
DeferredOpenKind mDeferredOpen;
2019-08-23 11:56:54 -07:00
String mDeferredOpenFileName;
2024-08-27 05:48:22 +02:00
public class ExecutionCmd
{
public bool mOnlyIfNotFailed;
}
2019-08-23 11:56:54 -07:00
public class WriteEmitCmd : ExecutionCmd
{
public String mProjectName ~ delete _;
public String mPath ~ delete _;
}
2024-10-18 09:13:24 -04:00
public class EmsdkInstalledCmd : ExecutionCmd
{
public String mPath ~ delete _;
}
2024-08-27 05:48:22 +02:00
public class BuildCompletedCmd : ExecutionCmd
{
public Stopwatch mStopwatch ~ delete _;
2019-08-23 11:56:54 -07:00
public String mHotProjectName ~ delete _;
2024-08-27 05:48:22 +02:00
public bool mFailed;
2019-08-23 11:56:54 -07:00
#if IDE_C_SUPPORT
2024-08-27 05:48:22 +02:00
public HashSet<String> mClangCompiledFiles = new HashSet<String>() ~ DeleteContainerAndItems!(_);
2019-08-23 11:56:54 -07:00
#endif
2024-08-27 05:48:22 +02:00
}
class ProcessBfCompileCmd : ExecutionCmd
{
public BfPassInstance mBfPassInstance ~ delete _;
2024-08-27 05:48:22 +02:00
public CompileKind mCompileKind;
public Project mHotProject;
public Stopwatch mStopwatch ~ delete _;
2019-08-23 11:56:54 -07:00
public Profiler mProfiler;
public ProfileCmd mProfileCmd ~ delete _;
public bool mHadBeef;
public ~this()
{
}
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
class ProfileCmd : ExecutionCmd
{
public int mThreadId;
public String mDesc ~ delete _;
public int mSampleRate;
}
class OpenDebugConsoleCmd : ExecutionCmd
{
}
2024-08-27 05:48:22 +02:00
class StartDebugCmd : ExecutionCmd
{
public bool mConnectedToConsole;
public int32 mConsoleProcessId;
2019-08-23 11:56:54 -07:00
public bool mWasCompiled;
2020-03-23 12:07:05 -07:00
public bool mHotCompileEnabled;
public this()
{
}
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public class TargetCompletedCmd : ExecutionCmd
{
public Project mProject;
public bool mIsReady = true;
2024-08-27 05:48:22 +02:00
public this(Project project)
{
mProject = project;
}
public ~this()
{
}
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
2021-02-25 10:14:22 -08:00
public class ScriptCmd : ExecutionCmd
{
public String mCmd ~ delete _;
public String mPath ~ delete _;
}
public enum ArgsFileKind
2019-08-23 11:56:54 -07:00
{
None,
UTF8,
UTF16WithBom
}
2024-08-27 05:48:22 +02:00
public class ExecutionQueueCmd : ExecutionCmd
{
public String mReference ~ delete _;
2024-08-27 05:48:22 +02:00
public String mFileName ~ delete _;
public String mArgs ~ delete _;
public String mWorkingDir ~ delete _;
public Dictionary<String, String> mEnvVars ~ DeleteDictionaryAndKeysAndValues!(_);
2024-08-27 05:48:22 +02:00
public ArgsFileKind mUseArgsFile;
public int32 mParallelGroup = -1;
public bool mIsTargetRun;
public String mStdInData ~ delete _;
public RunFlags mRunFlags;
2024-08-27 05:48:22 +02:00
}
public List<ExecutionCmd> mExecutionQueue = new List<ExecutionCmd>() ~ DeleteContainerAndItems!(_);
public class ExecutionInstance
{
public SpawnedProcess mProcess /*~ delete _*/;
public List<String> mDeferredOutput = new List<String>() ~ DeleteContainerAndItems!(_);
public Stopwatch mStopwatch = new Stopwatch() ~ delete _;
public String mTempFileName ~ delete _;
public int32 mParallelGroup = -1;
public Task<String> mReadTask /*~ delete _*/;
public delegate void(Task<String>) mOnReadTaskComplete /*~ delete _*/;
public Task<String> mErrorTask /*~ delete _*/;
public delegate void(Task<String>) mOnErrorTaskComplete /*~ delete _*/;
2019-08-23 11:56:54 -07:00
public Monitor mMonitor = new Monitor() ~ delete _;
public String mStdInData ~ delete _;
2019-08-23 11:56:54 -07:00
public Thread mOutputThread;
public Thread mErrorThread;
public Thread mInputThread;
2019-08-23 11:56:54 -07:00
public int? mExitCode;
public bool mAutoDelete = true;
public bool mCanceled;
public bool mIsTargetRun;
public bool mDone;
public bool mSmartOutput;
2019-08-23 11:56:54 -07:00
public ~this()
{
delete mProcess;
/*if (mProcess != null)
mProcess.Close();*/
if (mInputThread != null)
mInputThread.Join();
delete mInputThread;
2019-08-23 11:56:54 -07:00
if (mOutputThread != null)
mOutputThread.Join();
delete mOutputThread;
2019-08-23 11:56:54 -07:00
if (mErrorThread != null)
mErrorThread.Join();
delete mErrorThread;
2019-08-23 11:56:54 -07:00
delete mReadTask;
delete mOnReadTaskComplete;
delete mErrorTask;
delete mOnErrorTaskComplete;
}
public void Cancel()
{
mCanceled = true;
mProcess.Kill(0, .KillChildren);
}
public void Release()
{
if (!mDone)
mAutoDelete = true;
else
delete this;
}
2024-08-27 05:48:22 +02:00
}
List<ExecutionInstance> mExecutionInstances = new List<ExecutionInstance>() ~ DeleteContainerAndItems!(_);
public int32 mHotIndex = 0;
private int32 mStepCount;
private int32 mNoDebugMessagesTick;
2019-08-23 11:56:54 -07:00
public class DeferredShowSource
{
public String mFilePath ~ delete _;
public int32 mShowHotIdx;
public int32 mRefHotIdx;
public int32 mLine;
public int32 mColumn;
public LocatorType mHilitePosition;
public bool mShowTemp;
}
public DeferredShowSource mDeferredShowSource ~ delete _;
2024-08-27 05:48:22 +02:00
public bool IsCompiling
{
get
{
return (mExecutionInstances.Count > 0) || (mExecutionQueue.Count > 0) || (mBuildContext != null);
}
}
2019-08-23 11:56:54 -07:00
public bool EnableGCCollect
{
get
{
return mEnableGCCollect;
}
set
{
mEnableGCCollect = value;
//GC.SetAutoCollectPeriod(mEnableGCCollect ? 2000 : -1);
GC.SetAutoCollectPeriod(mEnableGCCollect ? 20 : -1);
2024-08-27 05:48:22 +02:00
GC.SetCollectFreeThreshold(mEnableGCCollect ? 64 * 1024 * 1024 : -1);
2019-08-23 11:56:54 -07:00
}
}
public bool MenuHasFocus
{
get
{
for (var window in mWindows)
{
var widgetWindow = window as WidgetWindow;
if ((widgetWindow != null) && (widgetWindow.mHasFocus))
{
if (widgetWindow.mRootWidget is MenuContainer)
return true;
}
}
return false;
}
}
2024-10-25 07:41:53 -04:00
public Workspace.PlatformType CurrentPlatform
{
get
{
Workspace.Options workspaceOptions = GetCurWorkspaceOptions();
return Workspace.PlatformType.GetFromName(mPlatformName, workspaceOptions.mTargetTriple);
}
}
2024-08-27 05:48:22 +02:00
[CallingConvention(.Stdcall), CLink]
static extern void IDEHelper_ProgramStart();
2024-08-27 05:48:22 +02:00
[CallingConvention(.Stdcall), CLink]
static extern void IDEHelper_ProgramDone();
2024-08-27 05:48:22 +02:00
public this()
{
ThreadPool.MaxStackSize = 8 * 1024 * 1024;
2024-03-18 05:43:27 -04:00
2024-08-27 05:48:22 +02:00
sApp = this;
2019-08-23 11:56:54 -07:00
gApp = this;
mMainThread = Thread.CurrentThread;
IDEHelper_ProgramStart();
2019-08-23 11:56:54 -07:00
#if !CLI
mDebugger = new DebugManager();
mVerb = .OpenOrNew;
#endif
//BfParser_Go();///
mScriptManager.AddTarget(this);
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
public static this()
{
//Profiler.StartSampling();
}
2024-08-27 05:48:22 +02:00
public void GetBfSystems(List<BfSystem> systems)
{
systems.Add(mBfBuildSystem);
2019-08-23 11:56:54 -07:00
if (mBfResolveSystem != null)
2024-08-27 05:48:22 +02:00
systems.Add(mBfResolveSystem);
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public void GetBfCompilers(List<BfCompiler> compiler)
{
if (mBfBuildCompiler != null)
2024-08-27 05:48:22 +02:00
compiler.Add(mBfBuildCompiler);
if (mBfResolveCompiler != null)
2024-08-27 05:48:22 +02:00
compiler.Add(mBfResolveCompiler);
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public bool HaveSourcesChanged()
{
return mHaveSourcesChangedInternallySinceLastCompile || mHaveSourcesChangedExternallySinceLastCompile;
}
2019-08-23 11:56:54 -07:00
public void MarkWatchesDirty()
{
WithWatchPanels(scope (watchPanel) =>
{
watchPanel.MarkWatchesDirty(false);
});
}
2024-08-27 05:48:22 +02:00
void PCChanged()
{
var disasmPanel = TryGetDisassemblyPanel();
if (disasmPanel != null)
disasmPanel.mLocationDirty = true;
mDebugger.mCallStackDirty = true;
2019-08-23 11:56:54 -07:00
WithWatchPanels(scope (watchPanel) =>
{
watchPanel.MarkWatchesDirty(true, !mTargetHadFirstBreak);
});
2024-08-27 05:48:22 +02:00
mMemoryPanel.MarkViewDirty();
mCallStackPanel.MarkCallStackDirty();
mThreadPanel.MarkThreadsDirty();
mDebugger.CheckCallStack();
2019-08-23 11:56:54 -07:00
// If we do a "PCChanged" from the execution of a method in the Immediate panel, don't move
// the focus away from the Immediate edit widget
bool setFocus = true;
for (var window in mWindows)
{
var widgetWindow = window as WidgetWindow;
if ((widgetWindow != null) && (widgetWindow.mHasFocus))
{
if (widgetWindow.mFocusWidget is ImmediateWidget)
{
setFocus = false;
}
}
}
if (mRunningTestScript)
setFocus = true;
2024-08-27 05:48:22 +02:00
ShowPCLocation(mDebugger.mActiveCallStackIdx, false, false, false, setFocus);
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public ~this()
{
2019-08-23 11:56:54 -07:00
#if !CLI
2019-10-05 10:24:58 -07:00
if (!mStartedWithTestScript && !mForceFirstRun)
2019-08-23 11:56:54 -07:00
{
mSettings.Save();
SaveDefaultLayoutData();
}
2020-09-18 05:30:21 -07:00
mFileRecovery.WorkspaceClosed();
2019-08-23 11:56:54 -07:00
#endif
/*WithTabs(scope (tabButton) =>
2024-08-27 05:48:22 +02:00
{
2019-08-23 11:56:54 -07:00
var panel = tabButton.mContent as Panel;
if (panel != null)
{
if (!panel.mAutoDelete)
{
// Is part of the panel list, clear mContent so we don't double-delete
tabButton.mContent = null;
}
}
2024-08-27 05:48:22 +02:00
});*/
2019-08-23 11:56:54 -07:00
mixin RemoveAndDelete(var widget)
{
Widget.RemoveAndDelete(widget);
widget = null;
}
2020-01-12 09:21:50 -08:00
2019-08-23 11:56:54 -07:00
RemoveAndDelete!(mProjectPanel);
RemoveAndDelete!(mClassViewPanel);
RemoveAndDelete!(mOutputPanel);
2024-08-27 05:48:22 +02:00
#if BF_PLATFORM_WINDOWS
2024-07-19 10:31:33 +02:00
RemoveAndDelete!(mTerminalPanel);
RemoveAndDelete!(mConsolePanel);
2024-08-27 05:48:22 +02:00
#endif
2019-08-23 11:56:54 -07:00
RemoveAndDelete!(mImmediatePanel);
RemoveAndDelete!(mFindResultsPanel);
RemoveAndDelete!(mAutoWatchPanel);
RemoveAndDelete!(mWatchPanel);
RemoveAndDelete!(mMemoryPanel);
RemoveAndDelete!(mCallStackPanel);
RemoveAndDelete!(mBreakpointPanel);
2020-07-18 06:50:28 -07:00
RemoveAndDelete!(mDiagnosticsPanel);
2019-08-23 11:56:54 -07:00
RemoveAndDelete!(mModulePanel);
RemoveAndDelete!(mThreadPanel);
RemoveAndDelete!(mProfilePanel);
RemoveAndDelete!(mPropertiesPanel);
RemoveAndDelete!(mAutoCompletePanel);
2022-06-08 20:11:39 +02:00
RemoveAndDelete!(mBookmarksPanel);
2019-08-23 11:56:54 -07:00
if (mSymbolReferenceHelper != null)
mSymbolReferenceHelper.Close();
if (mBfBuildCompiler != null)
2021-12-28 10:34:31 -05:00
{
mBfBuildCompiler.RequestFastFinish();
2024-08-27 05:48:22 +02:00
mBfBuildCompiler.CancelBackground();
2021-12-28 10:34:31 -05:00
}
2019-08-23 11:56:54 -07:00
if (mBfResolveCompiler != null)
2021-12-28 10:34:31 -05:00
{
mBfResolveCompiler.RequestFastFinish();
2024-08-27 05:48:22 +02:00
mBfResolveCompiler.CancelBackground();
2021-12-28 10:34:31 -05:00
}
2020-01-06 15:07:13 -08:00
2019-08-23 11:56:54 -07:00
#if IDE_C_SUPPORT
2024-08-27 05:48:22 +02:00
mDepClang.CancelBackground();
mResolveClang.CancelBackground();
2019-08-23 11:56:54 -07:00
#endif
2024-08-27 05:48:22 +02:00
/*if (mMainBreakpoint != null)
{
mMainBreakpoint.Dispose();
mMainBreakpoint = null;
}*/
2019-08-23 11:56:54 -07:00
2025-03-19 11:01:28 -04:00
/*delete mBfBuildCompiler;
2024-08-27 05:48:22 +02:00
delete mBfBuildSystem;
delete mDepClang;
2025-03-19 11:01:28 -04:00
2024-08-27 05:48:22 +02:00
delete mBfResolveCompiler;
delete mBfResolveSystem;
delete mResolveClang;
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
delete mSpellChecker;
2019-08-23 11:56:54 -07:00
//base.Dispose();
2024-08-27 05:48:22 +02:00
delete mDebugger;
delete ThemeFactory.mDefault;*/
2019-08-23 11:56:54 -07:00
for (var val in mWindowDatas.Values)
delete val;
2020-01-29 16:36:41 -08:00
if (mErrorsPanel?.mParent != null)
mErrorsPanel.RemoveSelf();
2019-08-23 11:56:54 -07:00
ProcessIdleDeferredDeletes();
// Get rid of panels
ProcessDeferredDeletes();
delete mBfBuildCompiler;
delete mBfResolveCompiler;
#if IDE_C_SUPPORT
delete mResolveClang;
#endif
delete mSpellChecker;
2020-01-06 15:07:13 -08:00
//NOTE: this must be done after the resolve compiler has been destroyed
RemoveAndDelete!(mErrorsPanel);
2019-08-23 11:56:54 -07:00
// Clear these out, for delete ordering purposes
ProcessDeferredDeletes();
2020-10-15 10:44:29 -07:00
if (mDeferredRelaunchCmd != null)
{
ProcessStartInfo psi = scope ProcessStartInfo();
psi.SetFileNameAndArguments(mDeferredRelaunchCmd);
psi.UseShellExecute = false;
SpawnedProcess process = scope SpawnedProcess();
process.Start(psi).IgnoreError();
}
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
public bool IsCrashDump
{
get
{
return mCrashDumpPath != null;
}
}
2020-01-12 09:21:50 -08:00
void WithStandardPanels(delegate void(Panel panel) dlg)
{
dlg(mProjectPanel);
dlg(mClassViewPanel);
dlg(mOutputPanel);
2024-08-27 05:48:22 +02:00
#if BF_PLATFORM_WINDOWS
2024-07-19 10:31:33 +02:00
dlg(mTerminalPanel);
dlg(mConsolePanel);
2024-08-27 05:48:22 +02:00
#endif
2020-01-12 09:21:50 -08:00
dlg(mImmediatePanel);
dlg(mFindResultsPanel);
dlg(mAutoWatchPanel);
dlg(mWatchPanel);
dlg(mMemoryPanel);
dlg(mCallStackPanel);
dlg(mErrorsPanel);
dlg(mBreakpointPanel);
2020-07-18 06:50:28 -07:00
dlg(mDiagnosticsPanel);
2020-01-12 09:21:50 -08:00
dlg(mModulePanel);
dlg(mThreadPanel);
dlg(mProfilePanel);
dlg(mPropertiesPanel);
dlg(mAutoCompletePanel);
2022-06-09 18:23:57 +02:00
dlg(mBookmarksPanel);
2020-01-12 09:21:50 -08:00
}
2019-08-23 11:56:54 -07:00
public override void ShutdownCompleted()
{
base.ShutdownCompleted();
}
2024-08-27 05:48:22 +02:00
public override void Shutdown()
{
2019-08-23 11:56:54 -07:00
if ((mDebugger != null) && (mDebugger.mIsRunning))
{
mDebugger.DisposeNativeBreakpoints();
mDebugger.Detach();
mDebugger.mIsRunning = false;
mExecutionPaused = false;
}
2024-08-27 05:48:22 +02:00
base.Shutdown();
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public override void Run()
{
base.Run();
}
2019-08-23 11:56:54 -07:00
2020-02-07 08:44:06 -08:00
public void Cmd_ViewNew()
2019-08-23 11:56:54 -07:00
{
var sourceViewPanel = GetActiveSourceViewPanel();
if (sourceViewPanel != null)
{
ShowSourceFile(sourceViewPanel.mFilePath, sourceViewPanel.mProjectSource, SourceShowType.New);
}
}
public void IdleDeferDelete(Object obj)
{
mIdleDeferredDeletes.Add(obj);
}
void ProcessIdleDeferredDeletes()
{
if (((mBfBuildCompiler != null) && mBfBuildCompiler.HasQueuedCommands()) ||
((mBfResolveCompiler != null) && (mBfResolveCompiler.HasQueuedCommands()))
2024-08-27 05:48:22 +02:00
#if IDE_C_SUPPORT
|| (mDepClang.HasQueuedCommands()) ||
2019-08-23 11:56:54 -07:00
(mResolveClang.HasQueuedCommands())
#endif
2024-08-27 05:48:22 +02:00
)
2019-08-23 11:56:54 -07:00
return;
for (var obj in mIdleDeferredDeletes)
delete obj;
mIdleDeferredDeletes.Clear();
}
2024-08-27 05:48:22 +02:00
public void DoOpenFile()
{
2019-08-23 11:56:54 -07:00
#if !CLI
2022-01-07 12:51:57 -03:00
if (mDeferredOpenFileName != null)
{
for (let filePath in mDeferredOpenFileName.Split('|'))
{
AddRecentFile(.OpenedFile, filePath);
ShowSourceFile(scope String()..Reference(filePath));
}
DeleteAndNullify!(mDeferredOpenFileName);
return;
}
2019-08-23 11:56:54 -07:00
String fullDir = scope .();
let sourceViewPanel = GetActiveSourceViewPanel();
if (sourceViewPanel != null)
{
2022-03-08 06:27:06 -08:00
if (sourceViewPanel.mFilePath != null)
Path.GetDirectoryPath(sourceViewPanel.mFilePath, fullDir);
2019-08-23 11:56:54 -07:00
}
else if ((gApp.mDebugger.mRunningPath != null) && (!mWorkspace.IsInitialized))
{
Path.GetDirectoryPath(gApp.mDebugger.mRunningPath, fullDir);
}
var fileDialog = scope OpenFileDialog();
fileDialog.Title = "Open File";
fileDialog.SetFilter("All files (*.*)|*.*");
2019-08-23 11:56:54 -07:00
fileDialog.Multiselect = true;
fileDialog.ValidateNames = true;
if (!fullDir.IsEmpty)
fileDialog.InitialDirectory = fullDir;
mMainWindow.PreModalChild();
if (fileDialog.ShowDialog(GetActiveWindow()).GetValueOrDefault() == .OK)
{
for (String openFileName in fileDialog.FileNames)
{
//openFileName.Replace('\\', '/');
AddRecentFile(.OpenedFile, openFileName);
ShowSourceFile(openFileName);
}
}
#endif
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
2019-09-18 08:13:00 -07:00
public void OpenWorkspace(StringView openFileName)
{
CloseWorkspace();
mWorkspace.mDir = new String();
Path.GetDirectoryPath(openFileName, mWorkspace.mDir);
mWorkspace.mName = new String();
Path.GetFileName(mWorkspace.mDir, mWorkspace.mName);
CustomBuildProperties.Load();
2019-09-18 08:13:00 -07:00
LoadWorkspace(.OpenOrNew);
FinishShowingNewWorkspace();
}
2020-01-12 09:21:50 -08:00
public void DoOpenWorkspace(bool isCreating = false)
2024-08-27 05:48:22 +02:00
{
2019-08-23 11:56:54 -07:00
#if !CLI
if (mDeferredOpenFileName != null)
{
String prevFilePath = scope .(mDeferredOpenFileName);
mDeferredOpenFileName.Clear();
Path.GetActualPathName(prevFilePath, mDeferredOpenFileName);
2019-09-18 08:13:00 -07:00
OpenWorkspace(mDeferredOpenFileName);
2019-08-23 11:56:54 -07:00
DeleteAndNullify!(mDeferredOpenFileName);
return;
}
2020-01-12 09:21:50 -08:00
var folderDialog = scope FolderBrowserDialog(isCreating ? .Save : .Open);
2019-09-18 08:13:00 -07:00
var initialDir = scope String();
if (mInstallDir.Length > 0)
Path.GetDirectoryPath(.(mInstallDir, 0, mInstallDir.Length - 1), initialDir);
initialDir.Concat(Path.DirectorySeparatorChar, "Samples");
folderDialog.SelectedPath = initialDir;
2020-01-12 09:21:50 -08:00
//folderDialog.
2019-08-23 11:56:54 -07:00
if (folderDialog.ShowDialog(GetActiveWindow()).GetValueOrDefault() == .OK)
{
2020-05-11 10:16:24 -07:00
var selectedPath = scope String()..AppendF(folderDialog.SelectedPath);
2019-08-23 11:56:54 -07:00
selectedPath.Append(Path.DirectorySeparatorChar);
selectedPath.Append("BeefSpace.toml");
String.NewOrSet!(mDeferredOpenFileName, selectedPath);
mDeferredOpen = .Workspace;
}
#endif
}
public void DoOpenDebugSession(StringView filePath)
{
let data = scope StructuredData();
if (data.Load(filePath) case .Err)
{
CreateDefaultLayout();
2019-08-23 11:56:54 -07:00
OutputErrorLine("Failed to load debug session '{}'", filePath);
return;
}
AddRecentFile(.OpenedDebugSession, filePath);
NewDebugSession(true);
let project = mWorkspace.mProjects[0];
project.mProjectPath.Set(filePath);
LoadWorkspaceUserData(data);
using (data.Open("DebugSession"))
{
project.Deserialize(data);
}
}
public void DoOpenDebugSession()
2024-08-27 05:48:22 +02:00
{
2019-08-23 11:56:54 -07:00
#if !CLI
if (mDeferredOpenFileName != null)
{
CloseWorkspace();
DoOpenDebugSession(mDeferredOpenFileName);
FinishShowingNewWorkspace(false);
DeleteAndNullify!(mDeferredOpenFileName);
return;
}
var fileDialog = scope OpenFileDialog();
fileDialog.Title = "Open Debug Session";
fileDialog.SetFilter("Debug Session (*.bfdbg)|*.bfdbg|All files (*.*)|*.*");
fileDialog.ValidateNames = true;
2024-08-27 05:48:22 +02:00
mMainWindow.PreModalChild();
2019-08-23 11:56:54 -07:00
if (fileDialog.ShowDialog(GetActiveWindow()).GetValueOrDefault() == .OK)
{
for (String openFileName in fileDialog.FileNames)
{
String.NewOrSet!(mDeferredOpenFileName, openFileName);
2024-08-27 05:48:22 +02:00
mDeferredOpen = .DebugSession;
2019-08-23 11:56:54 -07:00
break;
}
}
#endif
}
public void OpenCrashDump(StringView path)
{
AddRecentFile(.OpenedCrashDump, path);
StopDebugging();
String.NewOrSet!(mCrashDumpPath, path);
CheckDebugVisualizers();
2019-08-23 11:56:54 -07:00
if (mDebugger.OpenMiniDump(mCrashDumpPath))
{
mDebugger.mIsRunning = true;
mExecutionPaused = false; // Make this false so we can detect a Pause immediately
}
else
{
2024-12-31 14:15:12 -08:00
Fail(scope String()..AppendF("Failed to load minidump '{0}'", mCrashDumpPath));
2019-08-23 11:56:54 -07:00
DeleteAndNullify!(mCrashDumpPath);
}
}
public void DoOpenCrashDump()
2024-08-27 05:48:22 +02:00
{
2019-08-23 11:56:54 -07:00
#if !CLI
2019-09-20 10:40:09 -07:00
if (mDeferredOpenFileName != null)
2019-08-23 11:56:54 -07:00
{
2019-09-20 10:40:09 -07:00
OpenCrashDump(mDeferredOpenFileName);
2019-08-23 11:56:54 -07:00
DeleteAndNullify!(mDeferredOpenFileName);
return;
2019-09-20 10:40:09 -07:00
}
2019-08-23 11:56:54 -07:00
var fileDialog = scope OpenFileDialog();
fileDialog.Title = "Open Crash Dump";
fileDialog.SetFilter("Crash Dump (*.dmp)|*.dmp|All files (*.*)|*.*");
fileDialog.ValidateNames = true;
2024-08-27 05:48:22 +02:00
mMainWindow.PreModalChild();
2019-08-23 11:56:54 -07:00
if (fileDialog.ShowDialog(GetActiveWindow()).GetValueOrDefault() == .OK)
{
for (String openFileName in fileDialog.FileNames)
{
OpenCrashDump(openFileName);
break;
}
}
#endif
}
public void CheckProjectRelativePath(String path)
{
if (path.Length < 2)
return;
if ((path[0] == '\\') || (path[0] == '/') || (path[1] == ':'))
return;
for (var project in mWorkspace.mProjects)
{
String testPath = scope String();
testPath.Append(project.mProjectDir);
2024-08-27 05:48:22 +02:00
testPath.Append(Path.DirectorySeparatorChar);
testPath.Append(path);
2019-08-23 11:56:54 -07:00
if (File.Exists(testPath))
{
path.Set(testPath);
return;
}
}
}
public void PerformAction(String cmdStr)
{
List<String> cmds = scope List<String>();
int strIdx = 0;
while (true)
{
String str = scope:: String();
Utils.ParseSpaceSep(cmdStr, ref strIdx, str);
if (str.IsEmpty)
break;
cmds.Add(str);
}
if (cmds.Count == 0)
return;
switch (cmds[0])
{
case "ShowCode":
if (cmds.Count < 3)
break;
if (var sourceViewPanel = GetActiveSourceViewPanel(true))
sourceViewPanel.RecordHistoryLocation();
2020-12-17 04:51:05 -08:00
StringView loc = cmds[2];
int line = -1;
int lineChar = -1;
2020-12-17 04:51:05 -08:00
int colonIdx = loc.IndexOf(':');
if (colonIdx != -1)
{
line = int.Parse(loc.Substring(0, colonIdx)).GetValueOrDefault();
lineChar = int.Parse(loc.Substring(colonIdx + 1)).GetValueOrDefault();
2020-12-17 04:51:05 -08:00
}
else
{
int32 charIdx = int32.Parse(loc).GetValueOrDefault();
var fileEditData = GetEditData(cmds[1]);
fileEditData?.mEditWidget?.mEditWidgetContent.GetLineCharAtIdx(charIdx, out line, out lineChar);
}
ShowSourceFileLocation(cmds[1], -1, -1, line, lineChar, .Smart, true);
2019-08-23 11:56:54 -07:00
case "ShowCodeAddr":
var sourceViewPanel = GetActiveSourceViewPanel(true);
if (sourceViewPanel != null)
sourceViewPanel.RecordHistoryLocation();
int64 addr = int64.Parse(cmds[1], System.Globalization.NumberStyles.HexNumber).GetValueOrDefault();
int64 inlineCallAddr = 0;
2022-06-01 11:57:49 -07:00
if (cmds.Count > 2)
inlineCallAddr = int64.Parse(cmds[2], System.Globalization.NumberStyles.HexNumber).GetValueOrDefault();
2019-08-23 11:56:54 -07:00
String fileName = scope String();
int lineNum = 0;
int column;
int hotIdx;
int defLineStart;
int defLineEnd;
mDebugger.GetCodeAddrInfo((int)addr, (int)inlineCallAddr, fileName, out hotIdx, out defLineStart, out defLineEnd, out lineNum, out column);
2019-08-23 11:56:54 -07:00
if (fileName.Length > 0)
{
int showHotIdx = -1;
if ((defLineStart == -1) || (sourceViewPanel == null) || (sourceViewPanel.HasTextChangedSinceCompile(defLineStart, defLineEnd, hotIdx)))
showHotIdx = hotIdx;
ShowSourceFileLocation(fileName, showHotIdx, hotIdx, lineNum, column, LocatorType.Always);
}
else if (addr != 0)
{
var disassemblyPanel = ShowDisassemblyPanel(true);
disassemblyPanel.Show(addr);
}
}
}
2024-08-27 05:48:22 +02:00
public Dialog QuerySaveFiles(List<String> changedList, WidgetWindow window = null)
{
Dialog aDialog;
if (changedList.Count == 1)
{
2024-12-31 14:15:12 -08:00
aDialog = ThemeFactory.mDefault.CreateDialog("Save file?", scope String()..AppendF("Save changes to '{0}' before closing?", changedList[0]), DarkTheme.sDarkTheme.mIconWarning);
2024-08-27 05:48:22 +02:00
}
else
{
String text = scope String("The following files have been modified: ");
text.Join(", ", changedList.GetEnumerator());
text.Append(". Save before closing?");
aDialog = ThemeFactory.mDefault.CreateDialog("Save files?", text, DarkTheme.sDarkTheme.mIconWarning);
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
return aDialog;
}
2019-08-23 11:56:54 -07:00
public bool CheckCloseWorkspace(BFWindow window, Action onSave, Action onNoSave, Action onCancel)
{
void DeleteDelegates()
{
delete onSave;
delete onNoSave;
delete onCancel;
}
if (mStopping)
{
DeleteDelegates();
return true;
}
List<String> changedList = scope List<String>();
if (mWorkspace.mHasChanged)
{
var path = scope String();
GetWorkspaceFileName(path);
var fileName = new String();
Path.GetFileName(path, fileName);
if (fileName.IsEmpty)
fileName.Append("Workspace");
2024-08-27 05:48:22 +02:00
changedList.Add(fileName);
2019-08-23 11:56:54 -07:00
}
for (var project in mWorkspace.mProjects)
2024-08-27 05:48:22 +02:00
if (project.mHasChanged)
2019-08-23 11:56:54 -07:00
{
var fileName = new String();
Path.GetFileName(project.mProjectPath, fileName);
if (fileName.IsEmpty)
fileName.Append(project.IsDebugSession ? "Debug Session" : "Project");
2024-08-27 05:48:22 +02:00
changedList.Add(fileName);
2019-08-23 11:56:54 -07:00
}
mWorkspace.WithProjectItems(scope (projectItem) =>
{
var projectSource = projectItem as ProjectSource;
if (projectSource != null)
{
if ((projectSource.mEditData != null) && (projectSource.mEditData.HasTextChanged()))
{
var fileName = new String();
2024-08-27 05:48:22 +02:00
Path.GetFileName(projectSource.mPath, fileName);
2019-08-23 11:56:54 -07:00
changedList.Add(fileName);
}
}
2024-08-27 05:48:22 +02:00
});
2019-08-23 11:56:54 -07:00
WithSourceViewPanels(scope (sourceViewPanel) =>
2024-08-27 05:48:22 +02:00
{
if (sourceViewPanel.HasUnsavedChanges())
2019-08-23 11:56:54 -07:00
{
var fileName = scope String();
Path.GetFileName(sourceViewPanel.mFilePath, fileName);
2022-01-28 08:54:00 -05:00
if (fileName.IsWhiteSpace)
fileName.Set("untitled");
2019-08-23 11:56:54 -07:00
if (!changedList.Contains(fileName))
2024-08-27 05:48:22 +02:00
changedList.Add(new String(fileName));
2019-08-23 11:56:54 -07:00
}
2024-08-27 05:48:22 +02:00
});
2019-08-23 11:56:54 -07:00
if (changedList.Count == 0)
{
DeleteDelegates();
if (!mRunningTestScript)
2024-08-27 05:48:22 +02:00
SaveWorkspaceUserData(false);
return true;
2019-08-23 11:56:54 -07:00
}
var aDialog = QuerySaveFiles(changedList);
2021-01-02 09:08:25 -08:00
ClearAndDeleteItems(changedList);
2019-08-23 11:56:54 -07:00
aDialog.mDefaultButton = aDialog.AddButton("Save", new (evt) =>
{
onSave();
} ~ delete onSave);
aDialog.AddButton("Don't Save", new (evt) =>
{
2019-09-18 08:13:00 -07:00
OutputLine("Changes not saved.");
2024-08-27 05:48:22 +02:00
onNoSave();
2019-08-23 11:56:54 -07:00
} ~ delete onNoSave);
aDialog.mEscButton = aDialog.AddButton("Cancel", new (evt) =>
{
if (onCancel != null)
onCancel();
} ~ delete onCancel);
aDialog.PopupWindow((WidgetWindow)window ?? mMainWindow);
return false;
}
2024-08-27 05:48:22 +02:00
public bool AllowClose(BFWindow window)
{
2019-08-23 11:56:54 -07:00
if (mRunningTestScript)
return true;
2024-08-27 05:48:22 +02:00
return CheckCloseWorkspace(window,
2019-08-23 11:56:54 -07:00
new () =>
{
// We use a close delay after saving so the user can see we actually saved before closing down
if (SaveAll())
mCloseDelay = 30;
},
new () =>
{
SaveWorkspaceUserData(false);
mMainWindow.Close(true);
}, null);
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public bool SecondaryAllowClose(BFWindow window)
{
2019-08-23 11:56:54 -07:00
if (mRunningTestScript)
return true;
void CloseTabs()
{
2024-08-27 05:48:22 +02:00
WithDocumentTabbedViewsOf(window, scope (tabbedView) =>
{
tabbedView.CloseTabs(false, true, true);
});
}
void CloseWindow()
{
mMainWindow.SetForeground();
window.Close(true);
}
2024-08-27 05:48:22 +02:00
List<String> changedList = scope List<String>();
2019-08-23 11:56:54 -07:00
defer ClearAndDeleteItems(changedList);
2024-08-27 05:48:22 +02:00
WithSourceViewPanelsOf(window, scope (sourceViewPanel) =>
{
if (sourceViewPanel.HasUnsavedChanges())
2019-08-23 11:56:54 -07:00
{
var fileName = new String();
Path.GetFileName(sourceViewPanel.mFilePath, fileName);
2024-08-27 05:48:22 +02:00
changedList.Add(fileName);
2019-08-23 11:56:54 -07:00
}
2024-08-27 05:48:22 +02:00
});
2024-08-27 05:48:22 +02:00
if (changedList.Count == 0)
{
CloseTabs();
2024-08-27 05:48:22 +02:00
return true;
}
var aDialog = QuerySaveFiles(changedList, (WidgetWindow)window);
aDialog.mDefaultButton = aDialog.AddButton("Save", new (evt) =>
{
bool hadError = false;
// We use a close delay after saving so the user can see we actually saved before closing down
var _this = this;
WithSourceViewPanelsOf(window, scope [&] (sourceViewPanel) =>
{
if ((!hadError) && (sourceViewPanel.HasUnsavedChanges()))
if (!_this.SaveFile(sourceViewPanel))
hadError = true;
});
if (hadError)
return;
CloseTabs();
CloseWindow();
});
aDialog.AddButton("Don't Save", new (evt) =>
{
var _this = this;
WithSourceViewPanelsOf(window, scope [&] (sourceViewPanel) =>
{
if (sourceViewPanel.HasUnsavedChanges())
_this.RevertSourceViewPanel(sourceViewPanel);
});
CloseTabs();
CloseWindow();
});
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
aDialog.mEscButton = aDialog.AddButton("Cancel");
aDialog.PopupWindow((WidgetWindow)window ?? mMainWindow);
return false;
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public SourceViewPanel GetActiveSourceViewPanel(bool includeLastActive = false, bool includeEmbeds = false)
{
2019-08-23 11:56:54 -07:00
if (mRunningTestScript)
return mLastActiveSourceViewPanel;
2024-08-27 05:48:22 +02:00
var activePanel = GetActiveDocumentPanel();
var sourceViewPanel = activePanel as SourceViewPanel;
2019-08-23 11:56:54 -07:00
if (sourceViewPanel != null)
sourceViewPanel = sourceViewPanel.GetActivePanel();
2019-08-23 11:56:54 -07:00
if ((mLastActiveSourceViewPanel != null) && (includeLastActive))
sourceViewPanel = mLastActiveSourceViewPanel.GetActivePanel();
if ((sourceViewPanel != null) && (includeEmbeds))
sourceViewPanel = sourceViewPanel.GetFocusedEmbeddedView();
return sourceViewPanel;
2024-08-27 05:48:22 +02:00
}
public TextPanel GetActiveTextPanel()
{
var activePanel = GetActivePanel();
return activePanel as TextPanel;
}
public void RefreshVisibleViews(SourceViewPanel excludeSourceViewPanel = null, CompilerBase.ViewRefreshKind viewRefreshKind = .FullRefresh)
{
WithSourceViewPanels(scope (sourceViewPanel) =>
{
if ((sourceViewPanel.mParent != null) && (sourceViewPanel != excludeSourceViewPanel))
{
2024-08-27 05:48:22 +02:00
if (viewRefreshKind == .Collapse)
sourceViewPanel.QueueCollapseRefresh();
2022-05-02 08:57:41 -07:00
else
sourceViewPanel.QueueFullRefresh(true);
}
2024-08-27 05:48:22 +02:00
});
}
2019-08-23 11:56:54 -07:00
public bool SaveFile(ProjectSource projectSource)
{
if (projectSource.mEditData.HasTextChanged())
{
for (var user in projectSource.mEditData.mEditWidget.mEditWidgetContent.mData.mUsers)
{
user.MarkDirty();
}
using (mFileWatcher.mMonitor.Enter())
{
2024-08-27 05:48:22 +02:00
String path = scope String();
2019-08-23 11:56:54 -07:00
projectSource.GetFullImportPath(path);
2024-08-27 05:48:22 +02:00
String text = scope String();
projectSource.mEditData.mEditWidget.GetText(text);
2019-11-22 12:27:13 -08:00
if (!SafeWriteTextFile(path, text, true, projectSource.mEditData.mLineEndingKind, scope (outStr) =>
{
projectSource.mEditData.BuildHash(outStr);
}))
{
2019-08-23 11:56:54 -07:00
return false;
2019-11-22 12:27:13 -08:00
}
2019-08-23 11:56:54 -07:00
mFileWatcher.OmitFileChange(path, text);
2024-08-27 05:48:22 +02:00
projectSource.mEditData.mLastFileTextVersion = projectSource.mEditData.mEditWidget.Content.mData.mCurTextVersionId;
2019-08-23 11:56:54 -07:00
#if IDE_C_SUPPORT
2024-08-27 05:48:22 +02:00
mDepClang.FileSaved(path);
2019-08-23 11:56:54 -07:00
#endif
2024-08-27 05:48:22 +02:00
if (mWakaTime != null)
mWakaTime.QueueFile(path, projectSource.mProject.mProjectName, true);
2019-08-23 11:56:54 -07:00
}
}
return true;
}
2019-11-22 12:27:13 -08:00
public bool SafeWriteTextFile(StringView path, StringView text, bool showErrors = true, LineEndingKind lineEndingKind = .Default, delegate void(StringView str) strOutDlg = null)
2019-08-23 11:56:54 -07:00
{
if (mWorkspace.IsSingleFileWorkspace)
{
if (mWorkspace.mCompositeFile.IsIncluded(path))
{
mWorkspace.mCompositeFile.Set(path, text);
return true;
}
}
StringView useText = text;
2020-03-23 12:07:05 -07:00
Debug.Assert(!text.Contains('\r'));
2019-08-23 11:56:54 -07:00
if (lineEndingKind != .Lf)
{
var str = scope:: String()..Append(text);
if (lineEndingKind == .Cr)
str.Replace('\n', '\r');
else
str.Replace("\n", "\r\n");
useText = str;
}
if (Utils.WriteTextFile(path, useText) case .Err)
{
if (gApp.mSettings.mEditorSettings.mPerforceAutoCheckout)
mSourceControl.Checkout(path);
Thread.Sleep(10);
if (Utils.WriteTextFile(path, useText) case .Err)
{
Thread.Sleep(100);
if (Utils.WriteTextFile(path, useText) case .Err)
{
if (showErrors)
2024-12-31 14:15:12 -08:00
Fail(scope String()..AppendF("Failed to write file '{0}'", path));
2024-08-27 05:48:22 +02:00
return false;
2019-08-23 11:56:54 -07:00
}
}
}
2019-11-22 12:27:13 -08:00
if (strOutDlg != null)
strOutDlg(useText);
for (var entry in mWorkspace.mProjectFileEntries)
{
if (entry.mPath == path)
entry.UpdateLastWriteTime();
}
2019-08-23 11:56:54 -07:00
return true;
}
public void SetEmbedCompiler(Settings.EditorSettings.CompilerKind emitCompiler)
{
gApp.mSettings.mEditorSettings.mEmitCompiler = emitCompiler;
mBfResolveCompiler?.QueueRefreshViewCommand(.Collapse);
if (emitCompiler == .Resolve)
mBfResolveCompiler?.QueueRefreshViewCommand(.FullRefresh);
}
2019-08-23 11:56:54 -07:00
public Result<void, FileError> LoadTextFile(String fileName, String outBuffer, bool autoRetry = true, delegate void() onPreFilter = null)
{
if (mWorkspace.IsSingleFileWorkspace)
{
if (mWorkspace.mCompositeFile.IsIncluded(fileName))
{
mWorkspace.mCompositeFile.Get(fileName, outBuffer);
if (onPreFilter != null)
onPreFilter();
return .Ok;
}
}
if (fileName.StartsWith("$Emit$"))
{
String useFileName = fileName;
BfCompiler compiler = (gApp.mSettings.mEditorSettings.mEmitCompiler == .Resolve) ? mBfResolveCompiler : mBfBuildCompiler;
if (useFileName.StartsWith("$Emit$Build$"))
{
useFileName = scope:: $"$Emit${useFileName.Substring("$Emit$Build$".Length)}";
compiler = mBfBuildCompiler;
}
else if (useFileName.StartsWith("$Emit$Resolve$"))
{
useFileName = scope:: $"$Emit${useFileName.Substring("$Emit$Resolve$".Length)}";
compiler = mBfResolveCompiler;
}
if (!compiler.IsPerformingBackgroundOperation())
compiler.GetEmitSource(useFileName, outBuffer);
2024-08-27 05:48:22 +02:00
if (onPreFilter != null)
onPreFilter();
return .Ok;
}
else if (fileName.StartsWith("$Emit"))
{
if (mDebugger.GetEmitSource(fileName, outBuffer))
return .Ok;
}
2019-08-23 11:56:54 -07:00
return Utils.LoadTextFile(fileName, outBuffer, autoRetry, onPreFilter);
}
public bool SaveFileAs(SourceViewPanel sourceViewPanel)
{
#if !CLI
String fullDir = scope .();
2022-01-28 08:54:00 -05:00
if (sourceViewPanel.mFilePath != null)
Path.GetDirectoryPath(sourceViewPanel.mFilePath, fullDir);
else if (!mWorkspace.mDir.IsWhiteSpace)
fullDir.Set(mWorkspace.mDir);
2019-08-23 11:56:54 -07:00
SaveFileDialog dialog = scope .();
dialog.SetFilter("All files (*.*)|*.*");
2019-08-23 11:56:54 -07:00
//dialog.ValidateNames = true;
if (!fullDir.IsEmpty)
dialog.InitialDirectory = fullDir;
2019-08-23 11:56:54 -07:00
if (sourceViewPanel.mFilePath != null)
{
String ext = scope .();
Path.GetExtension(sourceViewPanel.mFilePath, ext);
dialog.DefaultExt = ext;
String fileName = scope .();
Path.GetFileName(sourceViewPanel.mFilePath, fileName);
dialog.FileName = fileName;
}
//dialog.SetFilter("Beef projects (BeefProj.toml)|BeefProj.toml");
let activeWindow = GetActiveWindow();
dialog.OverwritePrompt = true;
if (dialog.ShowDialog(activeWindow).GetValueOrDefault() != .OK)
return false;
if (dialog.FileNames.Count != 1)
return false;
String filePath = dialog.FileNames[0];
// Same path?
if ((sourceViewPanel.mFilePath != null) && (Path.Equals(filePath, sourceViewPanel.mFilePath)))
return true;
if (!SaveFile(sourceViewPanel, filePath))
return false;
// Kinda hacky - we lose all view information...
CloseDocument(sourceViewPanel);
ShowSourceFile(filePath);
#endif
return true;
}
public bool SaveFile(FileEditData editData, String forcePath = null)
{
var lineEndingKind = LineEndingKind.Default;
if (editData != null)
{
for (var user in editData.mEditWidget.mEditWidgetContent.mData.mUsers)
2019-08-23 11:56:54 -07:00
{
user.MarkDirty();
2019-08-23 11:56:54 -07:00
}
lineEndingKind = editData.mLineEndingKind;
}
2019-08-23 11:56:54 -07:00
2025-03-19 11:01:28 -04:00
// Lock file watcher to synchronize the 'file changed' notification so we don't
// think a file was externally saved
using (mFileWatcher.mMonitor.Enter())
{
2024-08-27 05:48:22 +02:00
String path = forcePath ?? editData.mFilePath;
String text = scope String();
editData.mEditWidget.GetText(text);
if (!SafeWriteTextFile(path, text, true, lineEndingKind, scope (outStr) =>
2019-08-23 11:56:54 -07:00
{
editData.BuildHash(outStr);
}))
{
return false;
2019-08-23 11:56:54 -07:00
}
2020-11-04 09:50:41 -08:00
editData.GetFileTime();
editData.mLastFileTextVersion = editData.mEditWidget.Content.mData.mCurTextVersionId;
mFileWatcher.OmitFileChange(path, text);
if (forcePath == null)
2024-08-27 05:48:22 +02:00
{
for (var user in editData.mEditWidget.mEditWidgetContent.mData.mUsers)
2019-11-22 12:27:13 -08:00
{
if (var sourceEditWidgetContent = user as SourceEditWidgetContent)
{
var sourceViewPanel = sourceEditWidgetContent.mSourceViewPanel;
sourceViewPanel?.FileSaved();
}
2019-11-22 12:27:13 -08:00
}
}
2019-08-23 11:56:54 -07:00
#if IDE_C_SUPPORT
2024-08-27 05:48:22 +02:00
mDepClang.FileSaved(path);
2019-08-23 11:56:54 -07:00
#endif
}
return true;
}
2024-08-27 05:48:22 +02:00
public bool SaveFile(SourceViewPanel sourceViewPanel, String forcePath = null)
{
if ((sourceViewPanel.HasUnsavedChanges()) || (forcePath != null))
{
2020-11-10 06:03:50 -08:00
if (gApp.mSettings.mEditorSettings.mFormatOnSave)
{
sourceViewPanel.ReformatDocument(true);
}
if ((forcePath == null) && (sourceViewPanel.mFilePath == null))
{
return SaveFileAs(sourceViewPanel);
}
2019-08-23 11:56:54 -07:00
2020-04-30 10:34:37 -07:00
if (sourceViewPanel.mEditData == null)
{
sourceViewPanel.mEditData = GetEditData(forcePath, true, false);
sourceViewPanel.mEditData.mEditWidget = sourceViewPanel.mEditWidget;
}
if (!SaveFile(sourceViewPanel.mEditData, forcePath))
return false;
2024-08-27 05:48:22 +02:00
if (sourceViewPanel.mProjectSource != null)
{
if (mWakaTime != null)
{
String path = forcePath ?? sourceViewPanel.mFilePath;
2024-08-27 05:48:22 +02:00
mWakaTime.QueueFile(path, sourceViewPanel.mProjectSource.mProject.mProjectName, true);
}
}
}
return true;
}
public ProjectSource FindProjectItem(ProjectFolder projectFolder, String relPath)
{
for (var childItem in projectFolder.mChildItems)
{
ProjectSource projectSource = childItem as ProjectSource;
if (projectSource != null)
{
if (String.Equals(projectSource.mPath, relPath, Environment.IsFileSystemCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase))
return projectSource;
}
ProjectFolder childFolder = childItem as ProjectFolder;
if (childFolder != null)
{
projectSource = FindProjectItem(childFolder, relPath);
if (projectSource != null)
return projectSource;
}
}
return null;
}
public Project FindProjectByName(String projectName)
{
for (var project in mWorkspace.mProjects)
if (project.mProjectName == projectName)
return project;
return null;
}
public ProjectSource FindProjectSourceItem(String filePath)
{
for (var project in mWorkspace.mProjects)
{
String relPath = scope String();
project.GetProjectRelPath(filePath, relPath);
var projectItem = FindProjectItem(project.mRootFolder, relPath);
if (projectItem != null)
return projectItem;
}
return null;
}
2019-08-23 11:56:54 -07:00
///
2024-08-27 05:48:22 +02:00
void SerializeWindow(StructuredData data, WidgetWindow window)
{
data.Add("X", window.mNormX);
data.Add("Y", window.mNormY);
data.Add("Width", window.mNormWidth);
data.Add("Height", window.mNormHeight);
2019-09-27 13:03:47 -07:00
data.Add("ShowKind", window.mShowKind);
2024-08-27 05:48:22 +02:00
}
void SerializeTabbedView(StructuredData data, DarkTabbedView tabbedView, bool serializeDocs)
{
if (tabbedView == mActiveDocumentsTabbedView)
data.Add("DefaultDocumentsTabbedView", true);
data.Add("Type", "TabbedView");
using (data.CreateArray("Tabs"))
{
for (var tabWidget in tabbedView.mTabs)
{
2019-08-23 11:56:54 -07:00
if (!serializeDocs)
{
if (tabWidget.mContent is SourceViewPanel)
continue;
if (tabWidget.mContent is DisassemblyPanel)
continue;
2019-09-23 09:41:21 -07:00
if (tabWidget.mContent is WelcomePanel)
continue;
2023-06-04 13:41:44 +02:00
if (tabWidget.mContent is StartupPanel)
continue;
2019-08-23 11:56:54 -07:00
}
2024-08-27 05:48:22 +02:00
using (data.CreateObject())
{
2019-09-23 09:41:21 -07:00
var panel = tabWidget.mContent as Panel;
if (!panel.WantsSerialization)
continue;
2019-08-23 11:56:54 -07:00
if (tabWidget.mIsActive)
data.Add("Active", true);
2024-08-27 05:48:22 +02:00
data.Add("TabLabel", tabWidget.mLabel);
data.Add("TabWidth", tabWidget.mWantWidth / DarkTheme.sScale);
2022-11-22 01:24:49 +05:00
data.Add("TabIsPinned", tabWidget.mIsPinned);
2019-08-23 11:56:54 -07:00
if (var watchPanel = tabWidget.mContent as WatchPanel)
{
watchPanel.Serialize(data, serializeDocs);
}
2024-08-27 05:48:22 +02:00
else if (panel != null)
{
panel.Serialize(data);
}
else
{
var innerTabbedView = tabWidget.mContent as DarkTabbedView;
if (innerTabbedView != null)
{
SerializeTabbedView(data, innerTabbedView, serializeDocs);
}
}
}
}
}
}
void SerializeDockingFrame(StructuredData data, DockingFrame dockingFrame, bool serializeDocs)
{
data.Add("Type", "DockingFrame");
data.Add("SplitType", (int32)dockingFrame.mSplitType);
using (data.CreateArray("DockedWidgets"))
{
for (var dockedWidget in dockingFrame.mDockedWidgets)
{
using (data.CreateObject())
{
if (dockedWidget.mIsFillWidget)
data.Add("IsFillWidget", true);
2019-08-23 11:56:54 -07:00
if (!dockedWidget.mAutoClose)
data.Add("Permanent", true);
2024-08-27 05:48:22 +02:00
data.Add("RequestedWidth", dockedWidget.mRequestedWidth);
data.Add("RequestedHeight", dockedWidget.mRequestedHeight);
data.ConditionalAdd("SizePriority", dockedWidget.mSizePriority, 0);
if (dockedWidget is DarkDockingFrame)
{
var innerDockingFrame = (DarkDockingFrame)dockedWidget;
SerializeDockingFrame(data, innerDockingFrame, serializeDocs);
}
if (dockedWidget is DarkTabbedView)
{
var tabbedView = (DarkTabbedView)dockedWidget;
SerializeTabbedView(data, tabbedView, serializeDocs);
}
}
}
}
}
2019-08-23 11:56:54 -07:00
bool SaveDefaultLayoutData()
{
2019-09-23 14:55:26 -07:00
if (mMainWindow == null)
return true;
2019-08-23 11:56:54 -07:00
StructuredData sd = scope StructuredData();
sd.CreateNew();
sd.Add("FileVersion", 1);
using (sd.CreateObject("MainWindow"))
{
2024-08-27 05:48:22 +02:00
SerializeWindow(sd, mMainWindow);
2019-08-23 11:56:54 -07:00
}
using (sd.CreateObject("MainDockingFrame"))
{
2024-08-27 05:48:22 +02:00
SerializeDockingFrame(sd, mDockingFrame, false);
2019-08-23 11:56:54 -07:00
}
String projectUserFileName = scope String();
GetDefaultLayoutDataFileName(projectUserFileName);
String dataStr = scope String();
sd.ToTOML(dataStr);
return SafeWriteTextFile(projectUserFileName, dataStr, true);
}
bool LoadDefaultLayoutData()
{
String projectUserFileName = scope String();
GetDefaultLayoutDataFileName(projectUserFileName);
2024-08-27 05:48:22 +02:00
2019-08-23 11:56:54 -07:00
StructuredData sd = scope .();
if (sd.Load(projectUserFileName) case .Err)
return false;
return LoadWorkspaceUserData(sd);
}
void SaveWorkspaceUserData(StructuredData sd)
{
sd.Add("FileVersion", 1);
sd.Add("LastConfig", mConfigName);
sd.Add("LastPlatform", mPlatformName);
using (sd.CreateObject("MainWindow"))
{
2020-01-12 09:21:50 -08:00
if (mMainWindow != null)
2024-08-27 05:48:22 +02:00
SerializeWindow(sd, mMainWindow);
2020-01-12 09:21:50 -08:00
else
{
sd.Add("X", mRequestedWindowRect.mX);
sd.Add("Y", mRequestedWindowRect.mY);
sd.Add("Width", mRequestedWindowRect.mWidth);
sd.Add("Height", mRequestedWindowRect.mHeight);
sd.Add("ShowKind", mRequestedShowKind);
}
2019-08-23 11:56:54 -07:00
}
using (sd.CreateObject("MainDockingFrame"))
{
2024-08-27 05:48:22 +02:00
SerializeDockingFrame(sd, mDockingFrame, true);
2019-08-23 11:56:54 -07:00
}
using (sd.CreateArray("RecentFilesList"))
{
2024-08-27 05:48:22 +02:00
for (var recentFile in mRecentlyDisplayedFiles)
{
String relPath = scope .();
mWorkspace.GetWorkspaceRelativePath(recentFile, relPath);
sd.Add(relPath);
}
2019-08-23 11:56:54 -07:00
}
using (sd.CreateArray("Breakpoints"))
{
2024-08-27 05:48:22 +02:00
for (var breakpoint in mDebugger.mBreakpointList)
{
if ((breakpoint.mFileName != null) || (breakpoint.mIsMemoryBreakpoint) || (breakpoint.mSymbol != null))
{
using (sd.CreateObject())
{
if (breakpoint.mFileName != null)
{
String relPath = scope .();
mWorkspace.GetWorkspaceRelativePath(breakpoint.mFileName, relPath);
2024-08-27 05:48:22 +02:00
sd.Add("File", relPath);
sd.Add("Line", breakpoint.mLineNum);
sd.Add("Column", breakpoint.mColumn);
2019-08-23 11:56:54 -07:00
if (breakpoint.mInstrOffset != -1)
sd.Add("InstrOffset", breakpoint.mInstrOffset);
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
if (breakpoint.mSymbol != null)
sd.Add("Symbol", breakpoint.mSymbol);
2024-08-27 05:48:22 +02:00
if (breakpoint.mIsMemoryBreakpoint)
sd.Add("MemoryWatchExpression", breakpoint.mMemoryWatchExpression);
2019-08-23 11:56:54 -07:00
if (breakpoint.mCondition != null)
sd.Add("Condition", breakpoint.mCondition);
if (breakpoint.mLogging != null)
sd.Add("Logging", breakpoint.mLogging);
sd.ConditionalAdd("BreakAfterLogging", breakpoint.mBreakAfterLogging, false);
sd.ConditionalAdd("HitCountBreak", breakpoint.mHitCountBreakKind, .None);
sd.ConditionalAdd("HitCountTarget", breakpoint.mHitCountTarget, 0);
if (breakpoint.mDisabled)
sd.Add("Disabled", true);
if (breakpoint.mThreadId != -1)
sd.Add("HasThreadId", true);
2024-08-27 05:48:22 +02:00
}
}
}
2019-08-23 11:56:54 -07:00
}
2024-08-27 05:48:22 +02:00
2022-06-09 12:25:48 +02:00
using (sd.CreateArray("BookmarkFolders"))
2019-08-23 11:56:54 -07:00
{
2022-06-09 12:25:48 +02:00
for (var folder in mBookmarkManager.mBookmarkFolders)
{
2024-08-27 05:48:22 +02:00
using (sd.CreateObject())
{
sd.Add("Title", folder.mTitle);
2022-06-09 12:25:48 +02:00
using (sd.CreateArray("Bookmarks"))
{
for (var bookmark in folder.mBookmarkList)
{
2024-08-27 05:48:22 +02:00
if (bookmark.mFileName != null)
{
using (sd.CreateObject())
{
2022-06-09 12:25:48 +02:00
String relPath = scope .();
mWorkspace.GetWorkspaceRelativePath(bookmark.mFileName, relPath);
2024-08-27 05:48:22 +02:00
sd.Add("File", relPath);
sd.Add("Line", bookmark.mLineNum);
sd.Add("Column", bookmark.mColumn);
sd.Add("Title", bookmark.mTitle);
if (bookmark.mIsDisabled)
2022-06-09 12:25:48 +02:00
sd.Add("Disabled", true);
2024-08-27 05:48:22 +02:00
}
}
2022-06-09 12:25:48 +02:00
}
}
}
}
2019-08-23 11:56:54 -07:00
}
using (sd.CreateObject("DebuggerDisplayTypes"))
{
var displayTypeNames = scope String();
mDebugger.GetDisplayTypeNames(displayTypeNames);
2024-08-27 05:48:22 +02:00
var referenceIds = String.StackSplit!(displayTypeNames, '\n');
for (var referenceId in referenceIds)
{
if (!referenceId.StartsWith("0", StringComparison.Ordinal))
{
DebugManager.IntDisplayType intDisplayType;
DebugManager.MmDisplayType mmDisplayType;
2020-06-19 06:42:52 -07:00
DebugManager.FloatDisplayType floatDisplayType;
String formatStr = scope .();
2024-08-27 05:48:22 +02:00
mDebugger.GetDisplayTypes(referenceId, formatStr, out intDisplayType, out mmDisplayType, out floatDisplayType);
using (sd.CreateObject(referenceId))
{
if (!formatStr.IsEmpty)
sd.Add("FormatStr", formatStr);
2024-08-27 05:48:22 +02:00
sd.ConditionalAdd("IntDisplayType", intDisplayType);
sd.ConditionalAdd("MmDisplayType", mmDisplayType);
2020-06-19 06:42:52 -07:00
sd.ConditionalAdd("FloatDisplayType", floatDisplayType);
2024-08-27 05:48:22 +02:00
}
}
}
2019-08-23 11:56:54 -07:00
}
using (sd.CreateArray("StepFilters"))
{
2024-08-27 05:48:22 +02:00
for (var stepFilter in mDebugger.mStepFilterList.Values)
{
2019-08-23 11:56:54 -07:00
if (stepFilter.mIsGlobal)
continue;
if (stepFilter.mKind == .Filtered)
sd.Add(stepFilter.mFilter);
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
sd.RemoveIfEmpty();
}
using (sd.CreateArray("StepNotFilters"))
{
2024-08-27 05:48:22 +02:00
for (var stepFilter in mDebugger.mStepFilterList.Values)
{
2019-08-23 11:56:54 -07:00
if (stepFilter.mIsGlobal)
continue;
if (stepFilter.mKind == .NotFiltered)
sd.Add(stepFilter.mFilter);
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
sd.RemoveIfEmpty();
}
using (sd.CreateObject("OutputFilters"))
{
IDE.Debugger.DebugManager.OutputFilterFlags outputFilterFlags = 0;
if (gApp.mDebugger != null)
{
outputFilterFlags = gApp.mDebugger.GetOutputFilterFlags();
}
sd.Add("ModuleLoadMessages", outputFilterFlags.HasFlag(.ModuleLoadMessages));
sd.Add("ModuleUnloadMessages", outputFilterFlags.HasFlag(.ModuleUnloadMessages));
sd.Add("ProcessExitMessages", outputFilterFlags.HasFlag(.ProcessExitMessages));
sd.Add("ThreadCreateMessages", outputFilterFlags.HasFlag(.ThreadCreateMessages));
sd.Add("ThreadExitMessages", outputFilterFlags.HasFlag(.ThreadExitMessages));
sd.Add("SymbolLoadMessages", outputFilterFlags.HasFlag(.SymbolLoadMessages));
sd.Add("ProgramOutput", outputFilterFlags.HasFlag(.ProgramOutput));
}
2019-08-23 11:56:54 -07:00
}
2024-08-27 05:48:22 +02:00
bool SaveWorkspaceUserData(bool showErrors = true)
{
// Don't save if we didn't finish creating the workspace
if (mWorkspace.mNeedsCreate)
return false;
2020-04-30 10:34:37 -07:00
if (!mWorkspace.IsInitialized)
return false;
2019-08-23 11:56:54 -07:00
if (mWorkspace.IsDebugSession)
{
bool hasUnsavedProjects = false;
for (let project in mWorkspace.mProjects)
if (project.mHasChanged)
hasUnsavedProjects = true;
// If we purposely abandoned changes then don't save now
if (!hasUnsavedProjects)
SaveDebugSession();
return true;
}
2024-08-27 05:48:22 +02:00
StructuredData sd = scope StructuredData();
2019-08-23 11:56:54 -07:00
sd.CreateNew();
SaveWorkspaceUserData(sd);
2024-08-27 05:48:22 +02:00
String projectUserFileName = scope String();
GetWorkspaceUserDataFileName(projectUserFileName);
String jsonString = scope String();
2019-08-23 11:56:54 -07:00
sd.ToTOML(jsonString);
if (projectUserFileName.IsEmpty)
return false;
2024-08-27 05:48:22 +02:00
return SafeWriteTextFile(projectUserFileName, jsonString, showErrors);
}
bool GetWorkspaceUserDataFileName(String outResult)
{
2019-08-23 11:56:54 -07:00
if (mWorkspace.mDir == null)
return false;
if (mWorkspace.mCompositeFile != null)
{
outResult.Append(mWorkspace.mCompositeFile.mFilePath, ".bfuser");
}
else
{
2024-08-27 05:48:22 +02:00
outResult.Append(mWorkspace.mDir, "/BeefSpace_User.toml");
2019-08-23 11:56:54 -07:00
var legacyName = scope String(mWorkspace.mDir, "/BeefUser.toml");
if (File.Exists(legacyName))
{
File.Move(legacyName, outResult).IgnoreError();
}
}
// Temporary for legacy
if (!mInitialized)
{
String legacyName = scope String();
legacyName.Append(mWorkspace.mDir, "/", mWorkspace.mName, ".bfuser");
if (File.Exists(legacyName))
{
File.Move(legacyName, outResult).IgnoreError();
File.Delete(legacyName).IgnoreError();
}
}
return true;
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
2024-10-21 09:18:07 -04:00
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;
}
2019-08-23 11:56:54 -07:00
void GetDefaultLayoutDataFileName(String outResult)
{
outResult.Append(mInstallDir, "/DefaultLayout.toml");
}
2024-08-27 05:48:22 +02:00
bool GetWorkspaceFileName(String outResult)
{
2019-08-23 11:56:54 -07:00
if (mWorkspace.mDir == null)
return false;
//LEGACY:
/*if (!mInitialized)
{
outResult.Append(mWorkspace.mDir, "/", mWorkspace.mName, ".bfspace");
if (File.Exists(outResult))
return true;
outResult.Clear();
}*/
outResult.Append(mWorkspace.mDir, "/BeefSpace.toml");
IDEUtils.FixFilePath(outResult);
return true;
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
///
2024-08-27 05:48:22 +02:00
bool SaveWorkspace()
{
2019-11-30 13:28:40 -08:00
if (mWorkspace.mNeedsCreate)
mWorkspace.mNeedsCreate = false;
2019-08-23 11:56:54 -07:00
if (!mWorkspace.IsSingleFileWorkspace)
{
#if !CLI
if (mWorkspace.mDir == null)
{
let activeWindow = GetActiveWindow();
2024-08-27 05:48:22 +02:00
2019-08-23 11:56:54 -07:00
FolderBrowserDialog folderDialog = scope FolderBrowserDialog();
//folderDialog.SelectedPath = fullDir;
if (activeWindow != null)
activeWindow.PreModalChild();
if (folderDialog.ShowDialog(gApp.GetActiveWindow()).GetValueOrDefault() != .OK)
return false;
mWorkspace.mDir = new String(folderDialog.SelectedPath);
2024-08-27 05:48:22 +02:00
2019-08-23 11:56:54 -07:00
String workspaceFileName = scope .();
GetWorkspaceFileName(workspaceFileName);
if (File.Exists(workspaceFileName))
{
Fail(scope String()..AppendF("A Beef workspace already exists at '{0}'", mWorkspace.mDir));
DeleteAndNullify!(mWorkspace.mDir);
return false;
}
}
#endif
if (Directory.CreateDirectory(mWorkspace.mDir) case .Err)
{
2024-12-31 14:15:12 -08:00
Fail(scope String()..AppendF("Failed to create workspace directory '{0}'", mWorkspace.mDir));
2019-08-23 11:56:54 -07:00
return false;
}
}
2024-08-27 05:48:22 +02:00
StructuredData data = scope StructuredData();
2019-08-23 11:56:54 -07:00
data.CreateNew();
2024-08-27 05:48:22 +02:00
mWorkspace.Serialize(data);
2019-08-23 11:56:54 -07:00
String tomlString = scope String();
data.ToTOML(tomlString);
if (!mWorkspace.IsSingleFileWorkspace)
{
2024-08-27 05:48:22 +02:00
String workspaceFileName = scope String();
GetWorkspaceFileName(workspaceFileName);
2019-08-23 11:56:54 -07:00
if (!SafeWriteTextFile(workspaceFileName, tomlString))
{
2024-12-31 14:15:12 -08:00
Fail(scope String()..AppendF("Failed to write workspace file '{0}'", workspaceFileName));
2019-08-23 11:56:54 -07:00
return false;
}
}
else
{
// If it's just the FileVersion then don't save anything...
/*if (tomlString.Count('\n') < 2)
tomlString.Clear();*/
mWorkspace.mCompositeFile.Set("Workspace", tomlString);
}
mWorkspace.mHasChanged = false;
MarkDirty();
2024-08-27 05:48:22 +02:00
return true;
}
2019-08-23 11:56:54 -07:00
bool SaveDebugSession()
{
let project = mWorkspace.mProjects[0];
#if !CLI
if (project.mProjectPath.IsEmpty)
{
SaveFileDialog dialog = scope .();
let activeWindow = GetActiveWindow();
let workspaceOptions = GetCurWorkspaceOptions();
2019-08-23 11:56:54 -07:00
let options = GetCurProjectOptions(project);
String execCmd = scope .();
ResolveConfigString(mPlatformName, workspaceOptions, project, options, options.mDebugOptions.mCommand, "command", execCmd);
if (!execCmd.IsWhiteSpace)
{
2019-08-23 11:56:54 -07:00
String initialDir = scope .();
Path.GetDirectoryPath(execCmd, initialDir).IgnoreError();
if (!initialDir.IsWhiteSpace)
dialog.InitialDirectory = initialDir;
2019-08-23 11:56:54 -07:00
dialog.SetFilter("Debug Session (*.bfdbg)|*.bfdbg");
dialog.DefaultExt = ".bfdbg";
String fileName = scope .();
Path.GetFileNameWithoutExtension(execCmd, fileName);
2019-08-23 11:56:54 -07:00
if (!fileName.IsEmpty)
{
fileName.Append(".bfdbg");
dialog.FileName = fileName;
}
}
dialog.OverwritePrompt = true;
if (dialog.ShowDialog(activeWindow).GetValueOrDefault() != .OK)
return false;
2024-08-27 05:48:22 +02:00
2019-08-23 11:56:54 -07:00
project.mProjectPath.Set(dialog.FileNames[0]);
}
#endif
if (project.mProjectPath.IsEmpty)
return false;
StructuredData sd = scope StructuredData();
sd.CreateNew();
SaveWorkspaceUserData(sd);
using (sd.CreateObject("DebugSession"))
{
project.Serialize(sd);
}
String jsonString = scope String();
sd.ToTOML(jsonString);
if (!SafeWriteTextFile(project.mProjectPath, jsonString, true))
return false;
project.mHasChanged = false;
MarkDirty();
return true;
}
void LoadDebugSession()
{
/*String path = scope String();
if (!GetWorkspaceUserDataFileName(path))
return false;
var data = scope StructuredData();
if (data.Load(path) case .Err)
return false;
if (!LoadWorkspaceUserData(data))
return false;*/
}
2024-08-27 05:48:22 +02:00
public void AddProjectToWorkspace(Project project, bool addToProjectList = true)
{
2019-08-23 11:56:54 -07:00
if (addToProjectList)
2024-08-27 05:48:22 +02:00
mWorkspace.mProjects.Add(project);
mBfBuildSystem.AddProject(project);
2019-08-23 11:56:54 -07:00
#if !CLI
if (mBfResolveSystem != null)
2024-08-27 05:48:22 +02:00
mBfResolveSystem.AddProject(project);
2019-08-23 11:56:54 -07:00
#endif
mWorkspace.ClearProjectNameCache();
if (!mWorkspace.mLoading)
{
for (var checkProject in mWorkspace.mProjects)
{
int idx = checkProject.mDependencies.FindIndex(scope (dep) => dep.mProjectName == project.mProjectName);
2024-08-27 05:48:22 +02:00
if (idx != -1)
ProjectOptionsChanged(checkProject);
}
ProjectOptionsChanged(project, false);
}
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public void AddNewProjectToWorkspace(Project project, VerSpec verSpec = .None)
{
AddProjectToWorkspace(project);
mWorkspace.SetChanged();
2019-08-23 11:56:54 -07:00
var relPath = scope String();
Path.GetRelativePath(project.mProjectPath, mWorkspace.mDir, relPath);
relPath.Replace("\\", "/");
int lastSlash = relPath.LastIndexOf('/');
if (lastSlash != -1)
relPath.RemoveToEnd(lastSlash);
else
relPath.Set(".");
2019-08-23 11:56:54 -07:00
/*var endStr = "/BeefProj.toml";
if (relPath.EndsWith(endStr))
relPath.RemoveToEnd(relPath.Length - endStr.Length);*/
2020-05-11 10:16:24 -07:00
var projectSpec = new Workspace.ProjectSpec();
2019-08-23 11:56:54 -07:00
projectSpec.mProjectName = new .(project.mProjectName);
2021-02-25 08:10:21 -08:00
if (verSpec != .None)
2019-08-23 11:56:54 -07:00
{
2021-03-17 07:47:46 -04:00
projectSpec.mVerSpec = verSpec.Duplicate();
2019-08-23 11:56:54 -07:00
}
else
{
2021-02-25 08:10:21 -08:00
projectSpec.mVerSpec = .Path(new String(relPath));
2019-08-23 11:56:54 -07:00
}
mWorkspace.mProjectSpecs.Add(projectSpec);
var dep = new Project.Dependency();
dep.mProjectName = new .("corlib");
2021-02-25 08:10:21 -08:00
dep.mVerSpec = .SemVer(new .("*"));
2019-08-23 11:56:54 -07:00
project.mDependencies.Add(dep);
2024-08-27 05:48:22 +02:00
}
2024-10-21 09:18:07 -04:00
public void ProjectCreated(Project project)
{
mProjectPanel.InitProject(project, mProjectPanel.GetSelectedWorkspaceFolder());
mProjectPanel.Sort();
mWorkspace.FixOptions();
mWorkspace.mHasChanged = true;
mWorkspace.ClearProjectNameCache();
CurrentWorkspaceConfigChanged();
}
2024-08-27 05:48:22 +02:00
public Project CreateProject(String projName, String projDir, Project.TargetType targetType)
{
Project project = new Project();
project.mProjectName.Set(projName);
project.mProjectPath.Set(projDir);
Utils.GetDirWithSlash(project.mProjectPath);
2019-08-23 11:56:54 -07:00
project.mProjectPath.Append("BeefProj.toml");
2024-08-27 05:48:22 +02:00
project.mProjectDir.Set(projDir);
2019-08-23 11:56:54 -07:00
project.mGeneralOptions.mTargetType = targetType;
project.SetupDefault();
2024-08-27 05:48:22 +02:00
project.SetChanged();
2019-08-23 11:56:54 -07:00
AddNewProjectToWorkspace(project);
project.FinishCreate();
2024-10-21 09:18:07 -04:00
ProjectCreated(project);
2019-08-23 11:56:54 -07:00
return project;
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
void StopDebugging()
{
if (mDebugger.mIsRunning)
{
if (mDebugger.mIsComptimeDebug)
CancelBuild();
2019-08-23 11:56:54 -07:00
mDebugger.StopDebugging();
while (mDebugger.GetRunState() != .Terminated)
{
if (mDebugger.mIsComptimeDebug)
{
if (!mBfBuildCompiler.IsPerformingBackgroundOperation())
break;
}
2019-08-23 11:56:54 -07:00
mDebugger.Update();
}
mDebugger.mIsRunning = false;
mExecutionPaused = false;
}
mDebugger.DisposeNativeBreakpoints();
mWantsRehupCallstack = false;
if (mDebugger.mIsComptimeDebug)
mDebugger.Detach();
2019-08-23 11:56:54 -07:00
}
void CloseWorkspace()
{
StopDebugging();
mDebugger.Reset();
WithWatchPanels(scope (watchPanel) =>
{
watchPanel.Reset();
});
mMemoryPanel.MarkViewDirty();
mCallStackPanel.MarkCallStackDirty();
mThreadPanel.MarkThreadsDirty();
if (mBfResolveCompiler != null)
{
mBfResolveCompiler.RequestCancelBackground();
mBfResolveCompiler.CancelBackground();
delete mBfResolveCompiler;
delete mBfResolveSystem;
delete mBfResolveHelper;
}
mBfBuildCompiler.RequestCancelBackground();
mBfBuildCompiler.CancelBackground();
delete mBfBuildCompiler;
delete mBfBuildSystem;
if (!mNoResolve)
{
2024-08-27 05:48:22 +02:00
mBfResolveSystem = new BfSystem();
mBfResolveCompiler = mBfResolveSystem.CreateCompiler(true);
mBfResolveHelper = new BfResolveHelper();
2019-08-23 11:56:54 -07:00
}
mCompileSinceCleanCount = 0;
mBfBuildSystem = new BfSystem();
mBfBuildCompiler = mBfBuildSystem.CreateCompiler(false);
mBfBuildCompiler.ClearBuildCache();
/*var docPanels = scope List<Widget>();
WithTabs(scope [&] (tab) =>
{
2024-08-27 05:48:22 +02:00
var sourceViewPanel = tab.mContent as SourceViewPanel;
2019-08-23 11:56:54 -07:00
if (sourceViewPanel != null)
2025-03-19 11:01:28 -04:00
{
2019-08-23 11:56:54 -07:00
docPanels.Add(sourceViewPanel);
2025-03-19 11:01:28 -04:00
}
2019-08-23 11:56:54 -07:00
});
for (var docPanel in docPanels)
CloseDocument(docPanel);*/
while (mWindows.Count > 1)
mWindows.Back.Close(true);
void ResetPanel(Widget widget)
{
if (widget.mParent != null)
widget.RemoveSelf();
}
2021-12-21 07:13:47 -05:00
WithSourceViewPanels(scope (sourceViewPanel) =>
{
sourceViewPanel.Dispose();
});
2019-08-23 11:56:54 -07:00
if (!mRunningTestScript)
{
mActiveDocumentsTabbedView = null;
2020-01-12 09:21:50 -08:00
WithStandardPanels(scope (panel) =>
{
ResetPanel(panel);
});
2019-08-23 11:56:54 -07:00
mMainFrame.Reset();
}
mGlobalUndoManager.Clear();
mHaveSourcesChangedExternallySinceLastCompile = false;
mHaveSourcesChangedInternallySinceLastCompile = false;
mIsImmediateDebugExprEval = false;
//mActiveDocumentsTabbedView;
if (mConfigName.IsEmpty)
mConfigName.Set("Debug");
if (mPlatformName.IsEmpty)
mPlatformName.Set(sPlatform64Name);
mDockingFrame = mMainFrame.mDockingFrame;
//mMainFrame.AddedToParent;
2020-09-18 05:30:21 -07:00
#if !CLI
mFileRecovery.WorkspaceClosed();
#endif
2019-08-23 11:56:54 -07:00
delete mWorkspace;
mWorkspace = new Workspace();
2019-09-18 08:13:00 -07:00
2020-01-12 09:21:50 -08:00
mErrorsPanel.Clear();
2022-06-08 20:11:39 +02:00
mBookmarksPanel.Clear();
mBookmarkManager.Clear();
2024-10-21 09:18:07 -04:00
mPackMan.CancelAll();
2019-09-18 08:13:00 -07:00
OutputLine("Workspace closed.");
2019-08-23 11:56:54 -07:00
}
void FinishShowingNewWorkspace(bool loadUserData = true)
{
if ((loadUserData) && (!mRunningTestScript))
{
if (!LoadWorkspaceUserData())
{
2024-08-27 05:48:22 +02:00
CreateDefaultLayout();
}
2019-08-23 11:56:54 -07:00
}
WorkspaceLoaded();
UpdateTitle();
UpdateRecentDisplayedFilesMenuItems();
mProjectPanel.RebuildUI();
ShowPanel(mOutputPanel, false);
mMainFrame.RehupSize();
2023-06-08 10:06:14 -04:00
if ((mSettings.mUISettings.mShowStartupPanel) && (!mWorkspace.IsInitialized))
ShowStartup();
else
ShowStartupFile();
#if !CLI
if (mMainWindow != null)
mFileRecovery.CheckWorkspace();
#endif
2019-08-23 11:56:54 -07:00
}
void CloseWorkspaceAndSetupNew()
{
CloseWorkspace();
FinishShowingNewWorkspace();
}
// TryCloseWorkspaceAndSetupNew
[IDECommand]
void Cmd_CloseWorkspaceAndSetupNew()
{
bool done = CheckCloseWorkspace(mMainWindow,
new () =>
{
// We use a close delay after saving so the user can see we actually saved before closing down
if (SaveAll())
{
CloseWorkspaceAndSetupNew();
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
},
new () =>
{
CloseWorkspaceAndSetupNew();
}, null);
if (done)
{
CloseWorkspaceAndSetupNew();
}
//CloseWorkspace();
//FinishShowingNewWorkspace();
}
[IDECommand]
void CollapseAll()
{
GetActiveSourceEditWidgetContent()?.CollapseAll();
}
[IDECommand]
void CollapseToDefinition()
{
GetActiveSourceEditWidgetContent()?.CollapseToDefinition();
}
[IDECommand]
void CollapseRedo()
{
}
[IDECommand]
void CollapseToggle()
{
GetActiveSourceEditWidgetContent()?.CollapseToggle();
}
[IDECommand]
void CollapseToggleAll()
{
GetActiveSourceEditWidgetContent()?.CollapseToggleAll();
}
[IDECommand]
void CollapseUndo()
{
}
2024-08-27 05:48:22 +02:00
[IDECommand]
void DeleteAllRight()
{
GetActiveSourceEditWidgetContent()?.DeleteAllRight();
}
2019-08-23 11:56:54 -07:00
2020-06-13 15:55:08 -07:00
[IDECommand]
void DuplicateLine()
{
var sewc = GetActiveSourceEditWidgetContent();
if (sewc != null)
sewc.DuplicateLine();
}
2021-12-18 20:36:46 +02:00
[IDECommand]
void CommentBlock()
{
var sewc = GetActiveSourceEditWidgetContent();
if (sewc != null)
sewc.CommentBlock();
}
[IDECommand]
void CommentLines()
{
var sewc = GetActiveSourceEditWidgetContent();
if (sewc != null)
sewc.CommentLines();
}
2020-06-13 15:55:08 -07:00
[IDECommand]
2021-12-21 07:49:57 -05:00
void CommentToggle()
2020-06-13 15:55:08 -07:00
{
var sewc = GetActiveSourceEditWidgetContent();
if (sewc != null)
2021-12-21 07:53:08 -05:00
sewc.ToggleComment();
2020-06-13 15:55:08 -07:00
}
[IDECommand]
void UncommentSelection()
{
var sewc = GetActiveSourceEditWidgetContent();
if (sewc != null)
sewc.ToggleComment(false);
}
[IDECommand]
void ComplexIdSpan()
{
if (var sourceViewPanel = GetLastActiveDocumentPanel() as SourceViewPanel)
{
var sewc = sourceViewPanel.mEditWidget.mEditWidgetContent as SourceEditWidgetContent;
2024-08-27 05:48:22 +02:00
uint8[] newData = new uint8[sewc.mData.mTextLength * 4];
var idData = ref sewc.mData.mTextIdData;
/*idData.Prepare();
int encodeIdx = 0;
int decodeIdx = 0;
int charId = 1;
int charIdx = 0;
while (true)
{
2024-08-27 05:48:22 +02:00
int32 cmd = Utils.DecodeInt(idData.mData, ref decodeIdx);
if (cmd > 0)
{
charId = cmd;
Utils.EncodeInt(newData, ref encodeIdx, charId);
}
2024-08-27 05:48:22 +02:00
else
{
int32 spanSize = -cmd;
2025-03-19 11:01:28 -04:00
2024-08-27 05:48:22 +02:00
charId += spanSize;
charIdx += spanSize;
2024-08-27 05:48:22 +02:00
if (cmd == 0)
{
Utils.EncodeInt(newData, ref encodeIdx, 0);
break;
}
while (spanSize > 65)
{
Utils.EncodeInt(newData, ref encodeIdx, -64);
spanSize -= 64;
}
Utils.EncodeInt(newData, ref encodeIdx, -spanSize);
2024-08-27 05:48:22 +02:00
}
}*/
int encodeIdx = 0;
int sizeLeft = sewc.mData.mTextLength;
while (sizeLeft > 0)
{
int writeLength = Math.Min(sizeLeft, 64);
Utils.EncodeInt(newData, ref encodeIdx, sewc.mData.mNextCharId);
Utils.EncodeInt(newData, ref encodeIdx, -writeLength);
sewc.mData.mNextCharId += (.)writeLength;
sewc.mData.mNextCharId++;
sizeLeft -= writeLength;
}
Utils.EncodeInt(newData, ref encodeIdx, 0);
IdSpan newSpan = .(newData, (.)encodeIdx);
//Runtime.Assert(newSpan.Equals(idData));
idData.Dispose();
idData = newSpan;
}
}
2019-08-23 11:56:54 -07:00
public Result<void, StructuredData.Error> StructuredLoad(StructuredData data, StringView filePath)
{
if (mWorkspace.IsSingleFileWorkspace)
{
String dataStr = scope .();
mWorkspace.mCompositeFile.Get(filePath, dataStr);
Try!(data.LoadFromString(dataStr));
return .Ok;
}
return data.Load(filePath);
}
public Result<void> StructuredSave(StringView filePath, StringView contents)
{
if (mWorkspace.IsSingleFileWorkspace)
{
mWorkspace.mCompositeFile.Set(filePath, contents);
return .Ok;
}
if (gApp.SafeWriteTextFile(filePath, contents))
return .Ok;
else
return .Err;
}
2024-10-21 09:18:07 -04:00
public void CheckDependenciesLoaded()
{
for (var project in mWorkspace.mProjects)
project.CheckDependenciesLoaded();
}
void FlushDeferredLoadProjects(bool addToUI = false)
2019-12-01 10:19:00 -08:00
{
2021-03-02 06:29:53 -08:00
bool hasDeferredProjects = false;
2024-10-21 09:18:07 -04:00
bool loadFailed = false;
2021-03-02 06:29:53 -08:00
2019-12-01 10:19:00 -08:00
while (true)
{
bool hadLoad = false;
for (int projectIdx = 0; projectIdx < mWorkspace.mProjects.Count; projectIdx++)
{
var project = mWorkspace.mProjects[projectIdx];
2024-10-21 09:18:07 -04:00
if (project.mDeferState == .Searching)
{
if (mPackMan.mFailed)
{
// Just let it fail now
LoadFailed();
project.mDeferState = .None;
project.mFailed = true;
loadFailed = true;
}
else
{
hasDeferredProjects = true;
}
}
2021-02-25 08:10:21 -08:00
if ((project.mDeferState == .ReadyToLoad) || (project.mDeferState == .Pending))
2019-12-01 10:19:00 -08:00
{
hadLoad = true;
var projectPath = project.mProjectPath;
2025-03-19 11:01:28 -04:00
2021-02-25 08:10:21 -08:00
if (project.mDeferState == .Pending)
{
2021-03-02 06:29:53 -08:00
hasDeferredProjects = true;
2021-02-25 08:10:21 -08:00
project.mDeferState = .Searching;
}
else if (!project.Load(projectPath))
2019-12-01 10:19:00 -08:00
{
OutputErrorLine("Failed to load project '{0}' from '{1}'", project.mProjectName, projectPath);
LoadFailed();
project.mFailed = true;
}
AddProjectToWorkspace(project, false);
if (addToUI)
2025-03-20 10:04:41 -04:00
mProjectPanel?.InitProject(project, null);
2019-12-01 10:19:00 -08:00
}
}
if (!hadLoad)
break;
}
2021-03-02 06:29:53 -08:00
if (hasDeferredProjects)
2024-10-21 09:18:07 -04:00
{
2021-03-02 06:29:53 -08:00
mWorkspace.mProjectLoadState = .Preparing;
2024-10-21 09:18:07 -04:00
}
2021-03-02 06:29:53 -08:00
else
2024-10-21 09:18:07 -04:00
{
2021-03-02 06:29:53 -08:00
mWorkspace.mProjectLoadState = .Loaded;
2024-10-21 09:18:07 -04:00
SaveWorkspaceLockData();
CheckDependenciesLoaded();
}
if (loadFailed)
{
2025-03-20 10:04:41 -04:00
mProjectPanel?.RebuildUI();
2024-10-21 09:18:07 -04:00
}
}
public void CancelWorkspaceLoading()
{
mPackMan.CancelAll();
FlushDeferredLoadProjects();
2019-12-01 10:19:00 -08:00
}
2024-08-27 05:48:22 +02:00
protected void LoadWorkspace(BeefVerb verb)
{
2019-08-23 11:56:54 -07:00
scope AutoBeefPerf("IDEApp.LoadWorkspace");
AddRecentFile(.OpenedWorkspace, mWorkspace.mDir);
2024-08-27 05:48:22 +02:00
StructuredData data = null;
2019-08-23 11:56:54 -07:00
String workspaceFileName = scope String();
if (mWorkspace.IsSingleFileWorkspace)
{
if (mWorkspace.mCompositeFile.Load() case .Err)
{
OutputErrorLine("Failed to load workspace '{0}'", mWorkspace.mCompositeFile.FilePath);
}
workspaceFileName.Append("Workspace");
}
else
2024-08-27 05:48:22 +02:00
GetWorkspaceFileName(workspaceFileName);
data = scope StructuredData(); //.LoadFromFile(workspaceFileName).GetValueOrDefault();
2019-08-23 11:56:54 -07:00
LoadConfig();
bool isNew = false;
bool wantSave = false;
mWorkspace.mLoading = true;
defer
{
mWorkspace.mLoading = false;
}
2022-11-10 06:37:55 -08:00
var startupProjectName = scope String();
2024-08-27 05:48:22 +02:00
if (StructuredLoad(data, workspaceFileName) case .Err(let err))
{
2019-10-04 10:39:25 -07:00
mBeefConfig.Refresh();
2019-08-23 11:56:54 -07:00
switch (err)
{
case .FormatError(int lineNum):
OutputErrorLine("Workspace format error in '{0}' on line {1}", workspaceFileName, lineNum);
2019-08-23 11:56:54 -07:00
LoadFailed();
return;
case .FileError: // Assume 'file not found'
if (verb == .OpenOrNew)
2019-08-23 11:56:54 -07:00
{
isNew = true;
}
else if (verb == .New)
2019-08-23 11:56:54 -07:00
{
isNew = true;
wantSave = true;
}
else
{
#if CLI
OutputLineSmart("ERROR: Workspace '{0}' does not exist. Use the '-new' command line argument to create a new workspace.", workspaceFileName);
LoadFailed();
return;
#endif
}
default:
OutputErrorLine("Failed to load workspace '{0}'", workspaceFileName);
LoadFailed();
return;
}
//Directory.CreateDirectory(mWorkspace.mDir).IgnoreError();
2024-08-27 05:48:22 +02:00
//int lastSlashPos = mWorkspace.mDir.LastIndexOf(IDEUtils.cNativeSlash);
String projectName = mWorkspace.mName;
2020-01-12 09:21:50 -08:00
Debug.Assert(!projectName.IsWhiteSpace);
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
String projectPath = scope String(mWorkspace.mDir);
Utils.GetDirWithSlash(projectPath);
projectPath.Append("BeefProj.toml");
Project project = new Project();
2019-08-23 11:56:54 -07:00
mWorkspace.mProjects.Add(project);
2019-09-18 08:13:00 -07:00
project.mProjectPath.Set(projectPath);
2024-08-27 05:48:22 +02:00
if (!project.Load(projectPath))
2019-08-23 11:56:54 -07:00
{
project.mBeefGlobalOptions.mDefaultNamespace.Set(projectName);
project.mBeefGlobalOptions.mStartupObject.Clear();
project.mBeefGlobalOptions.mStartupObject.AppendF("{0}.Program", projectName);
project.mProjectName.Set(projectName);
project.SetupDefault();
project.mNeedsCreate = true;
project.mHasChanged = true;
project.mProjectDir.Set(mWorkspace.mDir);
2024-08-27 05:48:22 +02:00
project.FinishCreate(false);
2019-08-23 11:56:54 -07:00
2021-02-25 08:10:21 -08:00
VerSpec verSpec = .SemVer(new .("*"));
defer verSpec.Dispose();
2019-08-23 11:56:54 -07:00
switch (AddProject("corlib", verSpec))
{
case .Ok(let libProject):
var dep = new Project.Dependency();
dep.mProjectName = new String("corlib");
2021-02-25 08:10:21 -08:00
dep.mVerSpec = verSpec.Duplicate();
2019-08-23 11:56:54 -07:00
project.mDependencies.Add(dep);
default:
}
}
2020-05-11 10:16:24 -07:00
var projSpec = new Workspace.ProjectSpec();
2019-08-23 11:56:54 -07:00
projSpec.mProjectName = new String(project.mProjectName);
2021-02-25 08:10:21 -08:00
projSpec.mVerSpec = .Path(new String("."));
2019-08-23 11:56:54 -07:00
mWorkspace.mProjectSpecs.Add(projSpec);
2024-08-27 05:48:22 +02:00
mWorkspace.mStartupProject = project;
2019-08-23 11:56:54 -07:00
mWorkspace.mHasChanged = true;
2024-08-27 05:48:22 +02:00
AddProjectToWorkspace(project, false);
2019-08-23 11:56:54 -07:00
ShowPanel(mOutputPanel, false);
/*var str = scope String();
Font.StrEncodeColor(0xfffef860, str);
str.AppendF("Created new workspace in '{0}'", mWorkspace.mDir);
Font.StrEncodePopColor(str);
OutputLine(str);*/
OutputWarnLine("Created new workspace in '{0}'", mWorkspace.mDir);
2019-08-23 11:56:54 -07:00
if (wantSave)
{
SaveWorkspace();
project.Save();
}
else
{
mWorkspace.mNeedsCreate = true;
OutputLine("Use 'File\\Save All' to commit to disk.");
2019-08-23 11:56:54 -07:00
}
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
else
{
2024-10-21 09:18:07 -04:00
LoadWorkspaceLockData();
mWorkspace.mProjectFileEntries.Add(new .(workspaceFileName));
2019-08-23 11:56:54 -07:00
if (mVerb == .New)
{
OutputErrorLine("Workspace '{0}' already exists, but '-new' argument was specified.", workspaceFileName);
2019-08-23 11:56:54 -07:00
LoadFailed();
}
using (data.Open("Workspace"))
{
data.GetString("StartupProject", startupProjectName);
}
if (mWorkspace.IsSingleFileWorkspace)
{
var project = new Project();
project.mProjectName.Set("Program");
project.mGeneralOptions.mTargetType = .BeefConsoleApplication;
Path.GetDirectoryPath(mWorkspace.mCompositeFile.FilePath, project.mProjectDir);
project.DeferLoad("");
mWorkspace.mProjects.Add(project);
}
if (data.Contains("Projects"))
{
for (var projectName in data.Enumerate("Projects"))
{
2020-05-11 10:16:24 -07:00
var projSpec = new Workspace.ProjectSpec();
2019-08-23 11:56:54 -07:00
projSpec.mProjectName = new String(projectName);
mWorkspace.mProjectSpecs.Add(projSpec);
if (projSpec.mVerSpec.Parse(data) case .Err)
{
2025-03-13 08:08:54 -04:00
var errStr = scope String();
errStr.AppendF("Unable to parse version specifier for {0} in {1}", projectName, workspaceFileName);
Fail(errStr);
2019-08-23 11:56:54 -07:00
LoadFailed();
continue;
}
switch (AddProject(projectName, projSpec.mVerSpec))
{
case .Ok(let project):
project.mLocked = data.GetBool("Locked", project.mLockedDefault);
case .Err:
OutputLineSmart("ERROR: Unable to load project '{0}' specified in workspace", projectName);
LoadFailed();
default:
}
}
}
mWorkspace.Deserialize(data);
}
2019-12-01 10:19:00 -08:00
FlushDeferredLoadProjects();
2019-08-23 11:56:54 -07:00
2022-11-10 06:37:55 -08:00
for (var project in mWorkspace.mProjects)
{
if ((!startupProjectName.IsEmpty) && (StringView.Compare(startupProjectName, project.mProjectName, true) == 0))
{
mWorkspace.mStartupProject = project;
}
}
2019-08-23 11:56:54 -07:00
mWorkspace.FinishDeserialize(data);
2024-08-27 05:48:22 +02:00
mWorkspace.FixOptions(mConfigName, mPlatformName);
2019-08-23 11:56:54 -07:00
#if !CLI
if (mBfResolveCompiler != null)
2024-08-27 05:48:22 +02:00
mBfResolveCompiler.QueueSetWorkspaceOptions(null, 0);
2019-08-23 11:56:54 -07:00
#endif
2020-09-17 14:15:38 -07:00
String relaunchCmd = scope .();
2020-10-15 10:44:29 -07:00
GetRelaunchCmd(true, relaunchCmd);
2020-09-17 14:15:38 -07:00
Platform.BfpSystem_SetCrashRelaunchCmd(relaunchCmd);
2019-08-23 11:56:54 -07:00
MarkDirty();
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
protected void ReloadWorkspace()
{
SaveWorkspaceUserData(false);
String workspaceDir = scope .(mWorkspace.mDir);
String workspaceName = scope .(mWorkspace.mName);
CloseWorkspace();
mWorkspace.mDir = new String(workspaceDir);
mWorkspace.mName = new String(workspaceName);
CustomBuildProperties.Load();
LoadWorkspace(.Open);
FinishShowingNewWorkspace();
}
2020-10-15 10:44:29 -07:00
public void GetRelaunchCmd(bool safeMode, String outRelaunchCmd)
{
if (mWorkspace.mDir != null)
{
outRelaunchCmd.Append("\"");
Environment.GetExecutableFilePath(outRelaunchCmd);
outRelaunchCmd.Append("\" -workspace=\"");
outRelaunchCmd.Append(mWorkspace.mDir);
outRelaunchCmd.Append("\"");
}
2020-10-15 10:44:29 -07:00
if (safeMode)
outRelaunchCmd.Append(" -safe");
}
2024-10-21 09:18:07 -04:00
public void RetryProjectLoad(Project project, bool reloadConfig)
2019-08-23 11:56:54 -07:00
{
2024-10-21 09:18:07 -04:00
if (reloadConfig)
LoadConfig();
2019-12-01 10:19:00 -08:00
2019-08-23 11:56:54 -07:00
var projectPath = project.mProjectPath;
if (!project.Load(projectPath))
{
Fail(scope String()..AppendF("Failed to load project '{0}' from '{1}'", project.mProjectName, projectPath));
LoadFailed();
project.mFailed = true;
2024-10-21 09:18:07 -04:00
FlushDeferredLoadProjects();
mProjectPanel?.RebuildUI();
2019-08-23 11:56:54 -07:00
}
else
{
2019-12-01 10:19:00 -08:00
FlushDeferredLoadProjects();
mWorkspace.FixOptions();
2019-08-23 11:56:54 -07:00
project.mFailed = false;
2024-10-21 09:18:07 -04:00
mProjectPanel?.RebuildUI();
2019-12-01 10:19:00 -08:00
CurrentWorkspaceConfigChanged();
2019-08-23 11:56:54 -07:00
}
}
public enum ProjectAddError
{
InvalidVersion,
InvalidVersionSpec,
LoadFailed,
NotFound
}
2021-02-25 08:10:21 -08:00
public Result<Project, ProjectAddError> AddProject(StringView projectName, VerSpec verSpec)
2019-08-23 11:56:54 -07:00
{
2021-02-25 08:10:21 -08:00
VerSpec useVerSpec = verSpec;
2019-08-23 11:56:54 -07:00
String verConfigDir = mWorkspace.mDir;
2019-12-01 10:19:00 -08:00
if (let project = mWorkspace.FindProject(projectName))
2024-10-21 09:18:07 -04:00
{
switch (useVerSpec)
{
case .Git(let url, let ver):
if (ver != null)
mPackMan.UpdateGitConstraint(url, ver);
default:
}
2019-12-01 10:19:00 -08:00
return project;
2024-10-21 09:18:07 -04:00
}
2021-02-25 08:10:21 -08:00
if (useVerSpec case .SemVer)
2019-08-23 11:56:54 -07:00
{
// First pass we just try to use the 'expected' project name
2024-08-27 05:48:22 +02:00
FindLoop:for (int pass < 2)
2019-08-23 11:56:54 -07:00
{
using (mBeefConfig.mRegistry.mMonitor.Enter())
{
BeefConfig.RegistryEntry matchedEntry = null;
for (int regEntryIdx = mBeefConfig.mRegistry.mEntries.Count - 1; regEntryIdx >= 0; regEntryIdx--)
{
var regEntry = mBeefConfig.mRegistry.mEntries[regEntryIdx];
if ((regEntry.mProjName == projectName) && (!regEntry.mParsedConfig))
mBeefConfig.mRegistry.ParseConfig(regEntry);
if (regEntry.mProjName == projectName)
{
// Prioritize a lib file over a non-lib
if ((matchedEntry == null) ||
((!matchedEntry.mTargetType.IsLib) && (regEntry.mTargetType.IsLib)))
matchedEntry = regEntry;
}
}
if (matchedEntry != null)
{
useVerSpec = matchedEntry.mLocation;
verConfigDir = matchedEntry.mConfigFile.mConfigDir;
break FindLoop;
}
}
mBeefConfig.mRegistry.WaitFor();
2019-08-23 11:56:54 -07:00
}
}
var project = new Project();
// For project locking, assume that only anything that is referenced with a path is editable
2021-02-25 08:10:21 -08:00
project.mLockedDefault = !(verSpec case .Path);
2019-08-23 11:56:54 -07:00
project.mLocked = project.mLockedDefault;
mWorkspace.mProjects.Add(project);
bool success = false;
defer
{
if (!success)
{
mWorkspace.mProjects.Remove(project);
delete project;
}
}
String projectFilePath = null;
2021-02-25 08:10:21 -08:00
bool isDeferredLoad = false;
2019-08-23 11:56:54 -07:00
2021-02-25 08:10:21 -08:00
switch (useVerSpec)
2019-08-23 11:56:54 -07:00
{
case .Path(let path):
2025-05-13 18:00:31 -07:00
Project.Options options = GetCurProjectOptions(project);
Workspace.Options workspaceOptions = GetCurWorkspaceOptions();
var resolvedPath = scope String();
ResolveConfigString(mPlatformName, workspaceOptions, project, options, path, "project path", resolvedPath);
var relPath = scope String(resolvedPath);
2019-08-23 11:56:54 -07:00
IDEUtils.FixFilePath(relPath);
if (!relPath.EndsWith(IDEUtils.cNativeSlash))
relPath.Append(IDEUtils.cNativeSlash);
var absPath = scope String();
Path.GetAbsolutePath(relPath, verConfigDir, absPath);
projectFilePath = scope:: String();
projectFilePath.Append(absPath, "BeefProj.toml");
case .SemVer(let semVer):
//
2021-02-25 08:10:21 -08:00
case .Git(let url, let ver):
2025-03-19 11:01:28 -04:00
2021-02-25 08:10:21 -08:00
var checkPath = scope String();
if (mPackMan.CheckLock(projectName, checkPath, var projectFailed))
2021-02-25 08:10:21 -08:00
{
projectFilePath = scope:: String(checkPath);
}
else
2024-10-21 09:18:07 -04:00
{
mPackMan.GetWithVersion(projectName, url, ver);
2021-02-25 08:10:21 -08:00
isDeferredLoad = true;
2024-10-21 09:18:07 -04:00
}
2019-08-23 11:56:54 -07:00
default:
Fail("Invalid version specifier");
return .Err(.InvalidVersionSpec);
}
2021-02-25 08:10:21 -08:00
if ((projectFilePath == null) && (!isDeferredLoad))
2019-08-23 11:56:54 -07:00
return .Err(.NotFound);
2021-02-25 08:10:21 -08:00
if (isDeferredLoad)
{
mWorkspace.mProjectLoadState = .Preparing;
}
2019-08-23 11:56:54 -07:00
project.mProjectName.Set(projectName);
project.DeferLoad(projectFilePath);
success = true;
2019-12-01 10:19:00 -08:00
mWorkspace.AddProjectToCache(project);
2019-08-23 11:56:54 -07:00
/*if (!project.Load(projectFilePath))
{
2024-12-31 14:15:12 -08:00
Fail(scope String()..AppendF("Failed to load project {0}", projectFilePath));
2019-08-23 11:56:54 -07:00
delete project;
return .Err(.LoadFailed);
}
success = true;
AddProjectToWorkspace(project, false);*/
return .Ok(project);
}
2024-10-21 09:18:07 -04:00
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);
}
2024-08-27 05:48:22 +02:00
protected void WorkspaceLoaded()
{
2019-08-23 11:56:54 -07:00
scope AutoBeefPerf("IDE.WorkspaceLoaded");
if (!Environment.IsFileSystemCaseSensitive)
{
// Make sure we have the correct actual path
String newPath = new String();
Path.GetActualPathName(mWorkspace.mDir, newPath);
delete mWorkspace.mDir;
mWorkspace.mDir = newPath;
}
2025-05-11 23:27:42 -03:00
else if (mWorkspace.mDir == null)
mWorkspace.mDir = new String();
2019-08-23 11:56:54 -07:00
List<String> platforms = scope List<String>();
if (IDEApp.sPlatform32Name != null)
platforms.Add(IDEApp.sPlatform32Name);
if (IDEApp.sPlatform64Name != null)
platforms.Add(IDEApp.sPlatform64Name);
2019-08-23 11:56:54 -07:00
List<String> configs = scope List<String>();
configs.Add("Debug");
configs.Add("Release");
configs.Add("Paranoid");
configs.Add("Test");
for (let platformName in platforms)
{
for (let configName in configs)
{
mWorkspace.FixOptions(configName, platformName);
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
}
mWorkspace.FixOptions(mConfigName, mPlatformName);
#if !CLI
if (mBfResolveSystem != null)
PreConfigureBeefSystem(mBfResolveSystem, mBfResolveCompiler);
2019-08-23 11:56:54 -07:00
#endif
2024-08-27 05:48:22 +02:00
for (var project in mWorkspace.mProjects)
{
2019-08-23 11:56:54 -07:00
project.mEnabled = IsProjectEnabled(project);
#if !CLI
if (mBfResolveSystem != null)
2024-08-27 05:48:22 +02:00
SetupBeefProjectSettings(mBfResolveSystem, mBfResolveCompiler, project);
2019-08-23 11:56:54 -07:00
#endif
2024-08-27 05:48:22 +02:00
project.mRootFolder.SortItems();
}
2019-08-23 11:56:54 -07:00
if (mWorkspace.IsSingleFileWorkspace)
{
AddToRecentDisplayedFilesList(CompositeFile.sMainFileName);
}
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
///
2024-08-27 05:48:22 +02:00
void DeserializeTabbedView(StructuredData data, IDETabbedView tabbedView)
{
if (data.GetBool("DefaultDocumentsTabbedView"))
mActiveDocumentsTabbedView = tabbedView;
2019-08-23 11:56:54 -07:00
SourceViewTabButton activeTab = null;
2024-08-27 05:48:22 +02:00
for ( data.Enumerate("Tabs"))
{
Panel panel = Panel.Create(data);
if (panel == null)
continue;
Debug.Assert(panel != null);
2019-08-23 11:56:54 -07:00
bool isActive = data.GetBool("Active");
2024-08-27 05:48:22 +02:00
var newTabButton = new SourceViewTabButton();
newTabButton.Label = "";
data.GetString("TabLabel", newTabButton.mLabel);
2022-11-22 01:24:49 +05:00
newTabButton.mIsPinned = data.GetBool("TabIsPinned");
2019-08-23 11:56:54 -07:00
newTabButton.mOwnsContent = panel.mAutoDelete;
newTabButton.mTabWidthOffset = panel.TabWidthOffset;
2024-08-27 05:48:22 +02:00
//newTabButton.mWantWidth = (float)Math.Round(data.GetFloat("TabWidth") * DarkTheme.sScale);
newTabButton.mHeight = tabbedView.mTabHeight;
newTabButton.mContent = panel;
tabbedView.AddTab(newTabButton, tabbedView.GetTabCount());
2019-08-23 11:56:54 -07:00
newTabButton.RehupScale(1.0f, 1.0f);
2024-08-27 05:48:22 +02:00
newTabButton.mCloseClickedEvent.Add(new () => DocumentCloseClicked(panel));
2019-08-23 11:56:54 -07:00
if (isActive)
activeTab = newTabButton;
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
if (activeTab != null)
activeTab.Activate(false);
2024-08-27 05:48:22 +02:00
}
void DeserializeDockingFrame(StructuredData data, DockingFrame dockingFrame)
{
dockingFrame.mSplitType = (DockingFrame.SplitType)data.GetInt("SplitType");
for (var _docWid in data.Enumerate("DockedWidgets"))
{
//for (int32 dockedWidgetIdx = 0; dockedWidgetIdx < data.Count; dockedWidgetIdx++)
2019-08-23 11:56:54 -07:00
//for (var dockedWidgetKV in data)
2024-08-27 05:48:22 +02:00
{
//using (data.Open(dockedWidgetIdx))
2019-08-23 11:56:54 -07:00
//using (data.Open(@dockedWidgetKV))
2024-08-27 05:48:22 +02:00
{
DockedWidget dockedWidget = null;
2019-08-23 11:56:54 -07:00
IDETabbedView tabbedView = null;
2024-08-27 05:48:22 +02:00
String type = scope String();
data.GetString("Type", type);
if (type == "DockingFrame")
{
var innerDockingFrame = new DarkDockingFrame();
DeserializeDockingFrame(data, innerDockingFrame);
dockedWidget = innerDockingFrame;
}
else if (type == "TabbedView")
{
tabbedView = CreateTabbedView();
DeserializeTabbedView(data, tabbedView);
dockedWidget = tabbedView;
}
dockedWidget.mParentDockingFrame = dockingFrame;
dockedWidget.mIsFillWidget = data.GetBool("IsFillWidget");
2019-08-23 11:56:54 -07:00
dockedWidget.mAutoClose = !data.GetBool("Permanent");
if (dockedWidget.mIsFillWidget)
dockedWidget.mHasFillWidget = true;
if (dockedWidget.mHasFillWidget)
dockingFrame.mHasFillWidget = true;
if ((dockedWidget.mIsFillWidget) && (tabbedView != null) && (mActiveDocumentsTabbedView == null))
mActiveDocumentsTabbedView = tabbedView;
dockedWidget.mHasFillWidget = data.GetBool("HasFillWidget");
dockedWidget.mSizePriority = data.GetFloat("SizePriority");
dockedWidget.mRequestedWidth = data.GetFloat("RequestedWidth");
dockedWidget.mRequestedHeight = data.GetFloat("RequestedHeight");
dockedWidget.mWidth = dockedWidget.mRequestedWidth;
dockedWidget.mHeight = dockedWidget.mRequestedHeight;
2024-08-27 05:48:22 +02:00
dockingFrame.AddWidget(dockedWidget);
dockingFrame.mDockedWidgets.Add(dockedWidget);
}
}
}
2019-08-23 11:56:54 -07:00
dockingFrame.Rehup();
2024-08-27 05:48:22 +02:00
dockingFrame.ResizeContent();
}
void DeserializeWindow(StructuredData data, WidgetWindow window)
{
int32 x = data.GetInt("X");
int32 y = data.GetInt("Y");
int32 width = data.GetInt("Width");
int32 height = data.GetInt("Height");
2019-08-23 11:56:54 -07:00
if ((width > 0) && (height > 0))
2019-09-27 13:03:47 -07:00
{
2019-08-23 11:56:54 -07:00
mRequestedWindowRect = Rect(x, y, width, height);
2019-09-27 13:03:47 -07:00
}
mRequestedShowKind = data.GetEnum<BFWindow.ShowKind>("ShowKind");
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
bool LoadWorkspaceUserData(StructuredData data)
{
2019-10-01 12:46:38 -07:00
//
2019-08-23 11:56:54 -07:00
{
String configName = scope String();
data.GetString("LastConfig", configName);
if (!configName.IsEmpty)
{
2024-08-27 05:48:22 +02:00
mConfigName.Set(configName);
}
2024-08-27 05:48:22 +02:00
2019-10-01 12:46:38 -07:00
String platformName = scope String();
data.GetString("LastPlatform", platformName);
if (!platformName.IsEmpty)
{
mPlatformName.Set(platformName);
2019-10-01 12:46:38 -07:00
}
2019-08-23 11:56:54 -07:00
}
using (data.Open("MainWindow"))
2024-08-27 05:48:22 +02:00
DeserializeWindow(data, mMainWindow);
2019-08-23 11:56:54 -07:00
if (mMainWindow == null)
mMainFrame.Resize(0, 0, mRequestedWindowRect.mWidth, mRequestedWindowRect.mHeight);
using (data.Open("MainDockingFrame"))
2024-08-27 05:48:22 +02:00
DeserializeDockingFrame(data, mDockingFrame);
2019-08-23 11:56:54 -07:00
2021-01-02 09:08:25 -08:00
ClearAndDeleteItems(mRecentlyDisplayedFiles);
2024-08-27 05:48:22 +02:00
for ( data.Enumerate("RecentFilesList"))
2019-08-23 11:56:54 -07:00
{
String relPath = scope String();
data.GetCurString(relPath);
IDEUtils.FixFilePath(relPath);
String absPath = new String();
mWorkspace.GetWorkspaceAbsPath(relPath, absPath);
2024-08-27 05:48:22 +02:00
mRecentlyDisplayedFiles.Add(absPath);
2019-08-23 11:56:54 -07:00
}
2019-10-01 12:46:38 -07:00
2019-08-23 11:56:54 -07:00
for (var _breakpoint in data.Enumerate("Breakpoints"))
2024-08-27 05:48:22 +02:00
{
String relPath = scope String();
data.GetString("File", relPath);
IDEUtils.FixFilePath(relPath);
String absPath = scope String();
mWorkspace.GetWorkspaceAbsPath(relPath, absPath);
2024-08-27 05:48:22 +02:00
int32 lineNum = data.GetInt("Line");
int32 column = data.GetInt("Column");
int32 instrOffset = data.GetInt("InstrOffset", -1);
String memoryWatchExpression = scope String();
data.GetString("MemoryWatchExpression", memoryWatchExpression);
2019-08-23 11:56:54 -07:00
Breakpoint breakpoint = null;
2024-08-27 05:48:22 +02:00
if (memoryWatchExpression.Length > 0)
breakpoint = mDebugger.CreateMemoryBreakpoint(memoryWatchExpression, (int)0, 0, null);
else if (absPath.Length > 0)
breakpoint = mDebugger.CreateBreakpoint(absPath, lineNum, column, instrOffset);
2019-08-23 11:56:54 -07:00
else
{
String symbol = scope .();
data.GetString("Symbol", symbol);
if (!symbol.IsEmpty)
breakpoint = mDebugger.CreateSymbolBreakpoint(symbol);
}
if (breakpoint != null)
{
String condition = scope String();
data.GetString("Condition", condition);
if (condition.Length > 0)
breakpoint.SetCondition(condition);
let logging = scope String();
data.GetString("Logging", logging);
bool breakAfterLogging = data.GetBool("BreakAfterLogging");
breakpoint.SetLogging(logging, breakAfterLogging);
let hitCountBreakKind = data.GetEnum<Breakpoint.HitCountBreakKind>("HitCountBreak");
int hitCountTarget = data.GetInt("HitCountTarget");
breakpoint.SetHitCountTarget(hitCountTarget, hitCountBreakKind);
if (data.GetBool("Disabled"))
mDebugger.SetBreakpointDisabled(breakpoint, true);
if (data.GetBool("HasThreadId"))
breakpoint.SetThreadId(0);
}
2024-08-27 05:48:22 +02:00
}
2022-06-09 12:25:48 +02:00
for (var _bookmarkFolder in data.Enumerate("BookmarkFolders"))
2019-08-23 11:56:54 -07:00
{
2022-06-08 20:11:39 +02:00
String title = scope String();
data.GetString("Title", title);
2022-06-09 12:25:48 +02:00
BookmarkFolder folder = null;
if (!String.IsNullOrWhiteSpace(title))
folder = mBookmarkManager.CreateFolder(title);
for (var _bookmark in data.Enumerate("Bookmarks"))
{
2024-08-27 05:48:22 +02:00
String relPath = scope String();
data.GetString("File", relPath);
2022-06-09 12:25:48 +02:00
IDEUtils.FixFilePath(relPath);
String absPath = scope String();
mWorkspace.GetWorkspaceAbsPath(relPath, absPath);
2024-08-27 05:48:22 +02:00
int32 lineNum = data.GetInt("Line");
int32 column = data.GetInt("Column");
2022-06-09 12:25:48 +02:00
String bookmarkTitle = scope String();
data.GetString("Title", bookmarkTitle);
2024-08-27 05:48:22 +02:00
bool isDisabled = data.GetBool("Disabled", false);
2022-06-08 20:11:39 +02:00
2024-08-27 05:48:22 +02:00
mBookmarkManager.CreateBookmark(absPath, lineNum, column, isDisabled, bookmarkTitle, folder);
2022-06-09 12:25:48 +02:00
}
2019-08-23 11:56:54 -07:00
}
2022-08-23 10:52:28 -07:00
// Legacy loading
for (var _bookmark in data.Enumerate("Bookmarks"))
{
2024-08-27 05:48:22 +02:00
String relPath = scope String();
data.GetString("File", relPath);
2022-08-23 10:52:28 -07:00
IDEUtils.FixFilePath(relPath);
String absPath = scope String();
mWorkspace.GetWorkspaceAbsPath(relPath, absPath);
2024-08-27 05:48:22 +02:00
int32 lineNum = data.GetInt("Line");
int32 column = data.GetInt("Column");
2022-08-23 10:52:28 -07:00
2024-08-27 05:48:22 +02:00
bool isDisabled = data.GetBool("Disabled", false);
2022-08-23 10:52:28 -07:00
2024-08-27 05:48:22 +02:00
mBookmarkManager.CreateBookmark(absPath, lineNum, column, isDisabled, null, null);
2022-08-23 10:52:28 -07:00
}
2022-08-23 10:45:57 -07:00
mBookmarkManager.RecalcCurId();
2019-08-23 11:56:54 -07:00
for (var referenceId in data.Enumerate("DebuggerDisplayTypes"))
{
var referenceIdStr = scope String(referenceId);
2024-08-27 05:48:22 +02:00
if (referenceIdStr.Length == 0)
referenceIdStr = null;
String formatStr = scope .();
data.GetString("FormatStr", formatStr);
2024-08-27 05:48:22 +02:00
var intDisplayType = data.GetEnum<DebugManager.IntDisplayType>("IntDisplayType");
var mmDisplayType = data.GetEnum<DebugManager.MmDisplayType>("MmDisplayType");
2020-06-19 06:42:52 -07:00
var floatDisplayType = data.GetEnum<DebugManager.FloatDisplayType>("FloatDisplayType");
2024-08-27 05:48:22 +02:00
mDebugger.SetDisplayTypes(referenceIdStr, formatStr, intDisplayType, mmDisplayType, floatDisplayType);
2019-08-23 11:56:54 -07:00
}
2024-08-27 05:48:22 +02:00
for ( data.Enumerate("StepFilters"))
2019-08-23 11:56:54 -07:00
{
String filter = scope String();
data.GetCurString(filter);
if (!filter.IsEmpty)
mDebugger.CreateStepFilter(filter, false, .Filtered);
}
2024-08-27 05:48:22 +02:00
for ( data.Enumerate("StepNotFilters"))
2019-08-23 11:56:54 -07:00
{
String filter = scope String();
data.GetCurString(filter);
if (!filter.IsEmpty)
mDebugger.CreateStepFilter(filter, false, .NotFiltered);
}
using (data.Open("OutputFilters"))
{
IDE.Debugger.DebugManager.OutputFilterFlags outputFilterFlags = 0;
outputFilterFlags |= data.GetBool("ModuleLoadMessages", false) ? .ModuleLoadMessages : 0;
outputFilterFlags |= data.GetBool("ModuleUnloadMessages", false) ? .ModuleUnloadMessages : 0;
outputFilterFlags |= data.GetBool("ProcessExitMessages", false) ? .ProcessExitMessages : 0;
outputFilterFlags |= data.GetBool("ThreadCreateMessages", false) ? .ThreadCreateMessages : 0;
outputFilterFlags |= data.GetBool("ThreadExitMessages", false) ? .ThreadExitMessages : 0;
outputFilterFlags |= data.GetBool("SymbolLoadMessages", false) ? .SymbolLoadMessages : 0;
outputFilterFlags |= data.GetBool("ProgramOutput", false) ? .ProgramOutput : 0;
if (gApp.mDebugger != null)
{
gApp.mDebugger.SetOutputFilterFlags(outputFilterFlags);
}
}
2019-08-23 11:56:54 -07:00
return true;
}
2024-08-27 05:48:22 +02:00
bool LoadWorkspaceUserData()
{
2019-08-23 11:56:54 -07:00
scope AutoBeefPerf("IDEApp.LoadWorkspaceUserData");
//return false;
String path = scope String();
if (!GetWorkspaceUserDataFileName(path))
return false;
2024-08-27 05:48:22 +02:00
var data = scope StructuredData();
if (data.Load(path) case .Err)
2019-08-23 11:56:54 -07:00
return false;
if (!LoadWorkspaceUserData(data))
return false;
return true;
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
///
2019-08-23 11:56:54 -07:00
public void RehupStepFilters()
{
mDebugger.SetStepOverExternalFiles(mStepOverExternalFiles);
}
2024-08-27 05:48:22 +02:00
public void SaveClangFiles()
{
WithSourceViewPanels(scope (sourceViewPanel) =>
{
if (sourceViewPanel.mIsClang)
SaveFile(sourceViewPanel);
});
}
2019-08-23 11:56:54 -07:00
[IDECommand]
2024-08-27 05:48:22 +02:00
public void SaveFile()
{
var sourceViewPanel = GetActiveSourceViewPanel();
if (sourceViewPanel != null)
SaveFile(sourceViewPanel);
}
2019-08-23 11:56:54 -07:00
[IDECommand]
public void SaveAs()
{
var sourceViewPanel = GetActiveSourceViewPanel();
if (sourceViewPanel != null)
{
SaveFileAs(sourceViewPanel);
}
}
[IDECommand]
public void OpenWorkspace()
{
mDeferredOpen = .Workspace;
}
void NewDebugSession(bool fromLoad)
{
if (!fromLoad)
CloseWorkspaceAndSetupNew();
mWorkspace.mIsDebugSession = true;
/*DebugSessionProperties workspaceProperties = new .();
workspaceProperties.PopupWindow(mMainWindow);
workspaceProperties.StartNew();*/
mPlatformName.Set(sPlatform64Name);
mConfigName.Set("Debug");
Project project = new .();
project.mGeneralOptions.mTargetType = .CustomBuild;
project.mProjectName.Set("Debug Session");
AddProjectToWorkspace(project);
mWorkspace.mStartupProject = project;
if (!fromLoad)
project.mHasChanged = true;
gApp.mWorkspace.FixOptions(mConfigName, mPlatformName);
mProjectPanel.RebuildUI();
if (!fromLoad)
{
ProjectProperties dialog = new .(project);
dialog.PopupWindow(mMainWindow);
dialog.mNewDebugSessionCountdown = 60;
}
}
[IDECommand]
public void Cmd_NewDebugSession()
{
#if !CLI
/*SaveFileDialog dialog = scope .();
2025-03-19 11:01:28 -04:00
2019-08-23 11:56:54 -07:00
let activeWindow = GetActiveWindow();
dialog.OverwritePrompt = true;
dialog.SetFilter("Debug Session (*.bfdbg)|*.bfdbg");
dialog.DefaultExt = ".bfdbg";
if (dialog.ShowDialog(activeWindow).GetValueOrDefault() != .OK)
return;*/
bool done = CheckCloseWorkspace(mMainWindow,
new () =>
{
// We use a close delay after saving so the user can see we actually saved before closing down
if (SaveAll())
{
NewDebugSession(false);
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
},
new () =>
{
NewDebugSession(false);
}, null);
if (done)
{
NewDebugSession(false);
}
#endif
}
[IDECommand]
public void Cmd_NewWorkspace()
{
mDeferredOpen = .NewWorkspaceOrProject;
2019-08-23 11:56:54 -07:00
}
[IDECommand]
public void Cmd_NewProject()
{
mProjectPanel.[Friend]AddNewProject();
}
[IDECommand]
public void Cmd_NewFile()
{
ShowSourceFile(null, null, .New);
}
[IDECommand]
public void Cmd_OpenProject()
{
mProjectPanel.[Friend]ImportProject();
}
[IDECommand]
public void OpenFile()
{
mDeferredOpen = .File;
}
[IDECommand]
public void OpenCrashDump()
{
mDeferredOpen = .CrashDump;
}
[IDECommand]
2024-08-27 05:48:22 +02:00
public bool SaveAll()
{
bool success = true;
WithSourceViewPanels(scope [&] (sourceViewPanel) =>
{
success &= SaveFile(sourceViewPanel);
});
2019-08-23 11:56:54 -07:00
mWorkspace.WithProjectItems(scope [&] (projectItem) =>
{
var projectSource = projectItem as ProjectSource;
if (projectSource != null)
{
if ((projectSource.mEditData != null) && (projectSource.mEditData.HasTextChanged()))
{
success &= SaveFile(projectSource);
}
}
});
2024-08-27 05:48:22 +02:00
for (var project in mWorkspace.mProjects)
{
if (project.mHasChanged)
2019-08-23 11:56:54 -07:00
{
if (project.IsDebugSession)
success &= SaveDebugSession();
else
2024-08-27 05:48:22 +02:00
project.Save();
2019-08-23 11:56:54 -07:00
}
2024-08-27 05:48:22 +02:00
}
if ((mWorkspace.IsInitialized) && (!mWorkspace.IsDebugSession) &&
2019-08-23 11:56:54 -07:00
((mWorkspace.mHasChanged) || (mWorkspace.mDir == null)))
2024-08-27 05:48:22 +02:00
success &= SaveWorkspace();
2019-08-23 11:56:54 -07:00
if (!mRunningTestScript)
2024-08-27 05:48:22 +02:00
{
2020-01-12 09:21:50 -08:00
#if !CLI
2019-08-23 11:56:54 -07:00
if (!mWorkspace.IsDebugSession)
success &= SaveWorkspaceUserData();
if (mSettings.mLoadedSettings)
mSettings.Save();
2020-01-12 09:21:50 -08:00
#endif
2019-08-23 11:56:54 -07:00
}
MarkDirty();
2024-08-27 05:48:22 +02:00
return success;
}
2019-08-23 11:56:54 -07:00
[IDECommand]
void Cmd_Exit()
{
mMainWindow.Close();
}
2024-08-27 05:48:22 +02:00
WidgetWindow GetCurrentWindow()
{
2019-08-23 11:56:54 -07:00
if (mRunningTestScript)
{
if (mLastActivePanel != null)
return mLastActivePanel.mWidgetWindow;
return mMainWindow;
}
2024-08-27 05:48:22 +02:00
for (var window in mWindows)
{
if (window.mHasFocus)
{
var returnWindow = window;
2019-08-23 11:56:54 -07:00
// With this "modal" flag, it caused errors to popup within a hoverwatch on a failed variable edit
2024-08-27 05:48:22 +02:00
while ((returnWindow.mParent != null) /*&& (returnWindow.mWindowFlags.HasFlag(BFWindow.Flags.Modal))*/)
returnWindow = returnWindow.mParent;
return (WidgetWindow)returnWindow;
}
}
return mMainWindow;
}
public Dialog Fail(String text, Widget addWidget = null, WidgetWindow parentWindow = null)
{
var text;
if (text.Contains('\t'))
{
text = scope:: String()..Append(text);
text.Replace("\t", " ");
}
2019-08-23 11:56:54 -07:00
// Always write to STDOUT even if we're running as a GUI, allowing cases like RunAndWait to pass us a stdout handle
2024-07-23 07:56:23 +02:00
Console.Error.WriteLine("ERROR: {0}", text).IgnoreError();
2019-08-23 11:56:54 -07:00
#if CLI
mFailed = true;
return null;
#endif
#unwarn
2020-04-28 12:55:35 -07:00
if ((mMainWindow == null) || (mShuttingDown))
2019-08-23 11:56:54 -07:00
{
mDeferredFails.Add(new String(text));
return null;
}
if (mRunningTestScript)
{
if (mScriptManager.IsErrorExpected(text))
2019-08-23 11:56:54 -07:00
{
OutputLine("Received expected error: {0}", text);
return null;
}
ShowOutput();
mFailed = true;
OutputLineSmart("ERROR: {0}", text);
2024-07-23 07:56:23 +02:00
Console.Error.WriteLine("ERROR: {0}", text).IgnoreError();
2019-08-23 11:56:54 -07:00
return null;
}
#unwarn
Debug.Assert(Thread.CurrentThread == mMainThread);
2024-08-27 05:48:22 +02:00
if (mMainWindow == null)
{
2024-12-31 14:15:12 -08:00
Internal.FatalError(scope String()..AppendF("FAILED: {0}", text));
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
Beep(MessageBeepType.Error);
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
Dialog dialog = ThemeFactory.mDefault.CreateDialog("ERROR", text, DarkTheme.sDarkTheme.mIconError);
dialog.mDefaultButton = dialog.AddButton("OK");
dialog.mEscButton = dialog.mDefaultButton;
2019-08-23 11:56:54 -07:00
dialog.mWindowFlags |= .Modal;
2024-08-27 05:48:22 +02:00
dialog.PopupWindow(parentWindow ?? GetCurrentWindow());
2019-08-23 11:56:54 -07:00
if (addWidget != null)
{
dialog.AddWidget(addWidget);
addWidget.mY = dialog.mHeight - 60;
addWidget.mX = 90;
}
return dialog;
2024-08-27 05:48:22 +02:00
}
2024-10-21 09:18:07 -04:00
public void MessageDialog(String title, String text, Image icon = null)
2024-08-27 05:48:22 +02:00
{
2024-10-21 09:18:07 -04:00
Dialog dialog = ThemeFactory.mDefault.CreateDialog(title, text, icon);
2024-08-27 05:48:22 +02:00
dialog.mDefaultButton = dialog.AddButton("OK");
dialog.mEscButton = dialog.mDefaultButton;
dialog.PopupWindow(mMainWindow);
}
public void DoQuickFind(bool isReplace)
{
var textPanel = GetActiveTextPanel();
if (textPanel != null)
{
textPanel.ShowQuickFind(isReplace);
2024-03-25 06:18:50 -04:00
return;
}
2019-08-23 11:56:54 -07:00
else
{
if (let activeWindow = GetActiveWindow())
2019-08-23 11:56:54 -07:00
{
var widget = activeWindow.mFocusWidget;
while (widget != null)
{
if (let watchStringEdit = widget as WatchStringEdit)
{
watchStringEdit.ShowQuickFind(isReplace);
return;
}
widget = widget.mParent;
}
}
}
2024-03-25 06:18:50 -04:00
var activePanel = GetActivePanel();
if (var watchPanel = activePanel as WatchPanel)
{
watchPanel.mListView.ShowFind();
}
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
[IDECommand]
public void ShowAbout()
{
Dialog dialog = new AboutDialog();
dialog.PopupWindow(mMainWindow);
}
[IDECommand]
public void Cmd_Document__Find()
{
DoQuickFind(false);
}
[IDECommand]
public void Cmd_Document__Replace()
{
DoQuickFind(true);
}
2024-08-27 05:48:22 +02:00
private void DoFindAndReplace(bool isReplace)
{
2019-08-23 11:56:54 -07:00
RecordHistoryLocation();
2024-08-27 05:48:22 +02:00
if (mFindAndReplaceDialog != null)
{
mFindAndReplaceDialog.mWidgetWindow.SetForeground();
return;
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
mFindAndReplaceDialog = new FindAndReplaceDialog(isReplace);
mFindAndReplaceDialog.PopupWindow(mMainWindow);
mFindAndReplaceDialog.mOnClosed.Add(new () => { mFindAndReplaceDialog = null; });
}
2019-08-23 11:56:54 -07:00
[IDECommand]
public void Cmd_Find()
{
DoFindAndReplace(false);
}
[IDECommand]
public void Cmd_Replace()
{
DoFindAndReplace(true);
}
[IDECommand]
public void Cmd_FindPrev()
{
DoFindNext(-1);
}
[IDECommand]
public void Cmd_FindNext()
{
DoFindNext(1);
}
[IDECommand]
public void CursorToLineEnd()
{
var sourceViewPanel = GetActiveSourceViewPanel();
if (sourceViewPanel != null)
sourceViewPanel.mEditWidget.mEditWidgetContent.CursorToLineEnd();
}
[IDECommand]
public void CursorToLineStart()
{
var sourceViewPanel = GetActiveSourceViewPanel();
if (sourceViewPanel != null)
sourceViewPanel.mEditWidget.mEditWidgetContent.CursorToLineStart(true);
}
2024-08-27 05:48:22 +02:00
public void DoFindNext(int32 dir = 1)
{
var textPanel = GetActiveTextPanel();
if (textPanel != null)
2019-08-23 11:56:54 -07:00
{
2024-08-27 05:48:22 +02:00
textPanel.FindNext(dir);
2019-08-23 11:56:54 -07:00
}
else
{
if (let activeWindow = GetActiveWindow())
2019-08-23 11:56:54 -07:00
{
var widget = activeWindow.mFocusWidget;
while (widget != null)
{
if (let watchStringEdit = widget as WatchStringEdit)
{
watchStringEdit.FindNext(dir);
return;
}
widget = widget.mParent;
}
}
}
2024-03-25 06:18:50 -04:00
var activePanel = GetActivePanel();
if (var watchPanel = activePanel as WatchPanel)
{
watchPanel.mListView.FindNext(dir);
}
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
void DoShowNextDocumentPanel()
{
var activeDoumentPanel = GetActiveDocumentPanel();
if ((activeDoumentPanel == null) && (mActiveDocumentsTabbedView != null))
{
var activeTab = mActiveDocumentsTabbedView.GetActiveTab();
activeTab.Activate();
return;
}
DarkTabbedView nextTabbedView = null;
DarkTabbedView firstTabbedView = null;
bool foundActiveTabbedView = false;
WithDocumentTabbedViews(scope [&] (tabbedView) =>
2024-08-27 05:48:22 +02:00
{
2019-08-23 11:56:54 -07:00
if (tabbedView.mIsFillWidget)
{
if (firstTabbedView == null)
firstTabbedView = tabbedView;
if (tabbedView == mActiveDocumentsTabbedView)
foundActiveTabbedView = true;
else if ((foundActiveTabbedView) && (nextTabbedView == null))
nextTabbedView = tabbedView;
}
2024-08-27 05:48:22 +02:00
});
2019-08-23 11:56:54 -07:00
if (nextTabbedView == null)
nextTabbedView = firstTabbedView;
if (nextTabbedView != null)
{
2020-05-20 06:44:21 -07:00
if (!nextTabbedView.mWidgetWindow.mHasFocus)
nextTabbedView.mWidgetWindow.SetForeground();
2019-08-23 11:56:54 -07:00
var activeTab = nextTabbedView.GetActiveTab();
2023-01-26 10:54:49 -05:00
if (activeTab != null)
2020-05-20 06:44:21 -07:00
{
2023-01-26 10:54:49 -05:00
activeTab.Activate();
if (let sourceViewPanel = activeTab.mContent as SourceViewPanel)
{
sourceViewPanel.HilitePosition(.Always);
}
2020-05-20 06:44:21 -07:00
}
2019-08-23 11:56:54 -07:00
}
}
[IDECommand]
public void Cmd_ShowCurrent()
{
var activePanel = GetActivePanel();
if (var sourceViewPanel = activePanel as SourceViewPanel)
{
sourceViewPanel.ShowCurrent();
}
else if (var disassemblyPanel = activePanel as DisassemblyPanel)
{
disassemblyPanel.GoToSource();
}
}
[IDECommand]
2024-08-27 05:48:22 +02:00
public void Cmd_GotoLine()
{
var sourceViewPanel = GetActiveSourceViewPanel();
if (sourceViewPanel != null)
{
2025-05-04 07:59:10 +05:00
sourceViewPanel.EditWidget.Content.RemoveSecondaryTextCursors();
2019-08-23 11:56:54 -07:00
sourceViewPanel.GotoLine();
return;
}
var activePanel = GetActivePanel();
if (let memoryPanel = activePanel as MemoryPanel)
{
memoryPanel.GotoAddress();
}
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
[IDECommand]
2024-08-27 05:48:22 +02:00
public void Cmd_GotoMethod()
{
var sourceViewPanel = GetActiveSourceViewPanel();
if (sourceViewPanel != null)
sourceViewPanel.GotoMethod();
}
2019-08-23 11:56:54 -07:00
[IDECommand]
public void Cmd_RenameItem()
{
let activePanel = GetActivePanel();
if (var projectPanel = activePanel as ProjectPanel)
2024-08-27 05:48:22 +02:00
{
2019-08-23 11:56:54 -07:00
projectPanel.TryRenameItem();
}
else if (var watchPanel = activePanel as WatchPanel)
{
watchPanel.TryRenameItem();
}
2022-06-08 20:11:39 +02:00
else if (var bookmarksPanel = activePanel as BookmarksPanel)
{
bookmarksPanel.TryRenameItem();
}
2019-08-23 11:56:54 -07:00
}
[IDECommand]
2024-08-27 05:48:22 +02:00
public void Cmd_RenameSymbol()
{
var sourceViewPanel = GetActiveSourceViewPanel();
if (sourceViewPanel != null)
sourceViewPanel.RenameSymbol();
}
2019-08-23 11:56:54 -07:00
[IDECommand]
public void Cmd_FindAllReferences()
{
var sourceViewPanel = GetActiveSourceViewPanel();
if (sourceViewPanel != null)
2024-08-27 05:48:22 +02:00
sourceViewPanel.FindAllReferences();
2019-08-23 11:56:54 -07:00
}
[IDECommand]
public void Cmd_FindClass()
{
var widgetWindow = GetCurrentWindow();
if (widgetWindow != null)
{
2024-08-27 05:48:22 +02:00
var dialog = new FindClassDialog();
dialog.PopupWindow(mMainWindow);
2019-08-23 11:56:54 -07:00
}
}
[IDECommand]
public void Cmd_ViewWhiteSpace()
{
mViewWhiteSpace.Toggle();
MarkDirty();
}
[IDECommand]
public void Cmd_ShowAutoComplete()
{
2019-09-29 07:44:23 -07:00
var sewc = GetActiveSourceEditWidgetContent();
if (sewc != null)
2019-08-23 11:56:54 -07:00
{
2019-09-29 07:44:23 -07:00
sewc.ShowAutoComplete(true);
2019-08-23 11:56:54 -07:00
}
}
[IDECommand]
public void Cmd_ShowFixit()
{
var sourceViewPanel = GetActiveSourceViewPanel();
if (sourceViewPanel != null)
sourceViewPanel.FixitAtCursor();
}
[IDECommand]
public void Cmd_PrevBookmark()
{
2022-06-09 12:25:48 +02:00
mBookmarkManager.PrevBookmark(false);
}
[IDECommand]
public void Cmd_PrevBookmarkInFolder()
{
mBookmarkManager.PrevBookmark(true);
2019-08-23 11:56:54 -07:00
}
[IDECommand]
public void Cmd_NextBookmark()
{
2022-06-09 12:25:48 +02:00
mBookmarkManager.NextBookmark(false);
}
[IDECommand]
public void Cmd_NextBookmarkInFolder()
{
mBookmarkManager.NextBookmark(true);
2019-08-23 11:56:54 -07:00
}
[IDECommand]
public void Cmd_ToggleBookmark()
{
var sourceViewPanel = GetActiveSourceViewPanel();
if (sourceViewPanel != null)
sourceViewPanel.ToggleBookmarkAtCursor();
}
[IDECommand]
public void Cmd_ClearBookmarks()
{
mBookmarkManager.Clear();
}
[IDECommand]
public void Cmd_Clean()
{
if (IsCompiling)
{
2020-03-27 11:16:18 +00:00
Fail("Cannot clean while compiling");
2019-08-23 11:56:54 -07:00
return; // Ignore
}
if (mDebugger.mIsRunning)
{
Fail("Cannot clean while running");
return; // Ignore
}
mWantsClean = true;
}
[IDECommand]
public void Cmd_CleanBeef()
{
if (IsCompiling)
{
2020-03-27 11:16:18 +00:00
Fail("Cannot clean while compiling");
2019-08-23 11:56:54 -07:00
return; // Ignore
}
if (mDebugger.mIsRunning)
{
Fail("Cannot clean while running");
return; // Ignore
}
mWantsBeefClean = true;
}
[IDECommand]
public void Cmd_CompileFile()
{
var sourceViewPanel = GetActiveSourceViewPanel();
if (sourceViewPanel != null)
2024-08-27 05:48:22 +02:00
{
if (sourceViewPanel.mProjectSource != null)
{
var filePath = scope String();
sourceViewPanel.mProjectSource.GetFullImportPath(filePath);
if ((filePath.EndsWith(".cpp", StringComparison.OrdinalIgnoreCase)) ||
(filePath.EndsWith(".c", StringComparison.OrdinalIgnoreCase)))
{
ShowPanel(mOutputPanel, false);
SaveFile(sourceViewPanel);
var project = sourceViewPanel.mProjectSource.mProject;
Project.Options options = GetCurProjectOptions(project);
if (options != null)
{
Workspace.Options workspaceOptions = GetCurWorkspaceOptions();
CompileSource(project, workspaceOptions, options, filePath);
}
}
}
2019-08-23 11:56:54 -07:00
}
}
[IDECommand]
public void Cmd_MatchBrace()
{
var sourceViewPanel = GetActiveSourceViewPanel();
if (sourceViewPanel != null)
sourceViewPanel.MatchBrace();
}
[IDECommand]
public void Cmd_MoveLine(VertDir dir)
{
var sewc = GetActiveSourceEditWidgetContent();
if (sewc != null)
sewc.MoveLine(dir);
}
[IDECommand]
public void Cmd_MoveStatement(VertDir dir)
{
var sewc = GetActiveSourceEditWidgetContent();
if (sewc != null)
sewc.MoveStatement(dir);
}
2019-08-23 11:56:54 -07:00
[IDECommand]
public void Cmd_GotoNextItem()
{
var curOutputPanel = mOutputPanel;
if ((mFindResultsPanel != null) && (mFindResultsPanel.mWidgetWindow != null))
{
2024-08-27 05:48:22 +02:00
if (mFindResultsPanel.mLastFocusAppUpdateCnt > curOutputPanel.mLastFocusAppUpdateCnt)
curOutputPanel = mFindResultsPanel;
2019-08-23 11:56:54 -07:00
}
curOutputPanel.GotoNextSourceReference();
}
2024-08-27 05:48:22 +02:00
2019-08-23 11:56:54 -07:00
[IDECommand]
public void Cmd_ZoomOut()
{
float scale = DarkTheme.sScale;
if (scale > 0.25f)
2019-08-23 11:56:54 -07:00
{
if (scale < 0)
scale -= 0.05f;
else //if (scale < 2.0f)
scale -= 0.10f;
SetScale(scale);
}
}
[IDECommand]
public void Cmd_ZoomIn()
{
float scale = DarkTheme.sScale;
if (scale < 4.0f)
{
if (scale < 0)
2024-08-27 05:48:22 +02:00
scale += 0.02f; //0.05f;
2019-08-23 11:56:54 -07:00
else //if (scale < 2.0f)
2024-08-27 05:48:22 +02:00
scale += 0.04f; //0.10f;
2019-08-23 11:56:54 -07:00
SetScale(scale);
}
}
[IDECommand]
public void Cmd_ShowFileExternally()
{
var sourceViewPanel = GetActiveSourceViewPanel();
if (sourceViewPanel != null)
{
ProcessStartInfo psi = scope ProcessStartInfo();
psi.SetFileName("/bin/OpenFileLine");
var args = scope String();
args.AppendF("{0} {1}", sourceViewPanel.mFilePath, sourceViewPanel.mEditWidget.mEditWidgetContent.CursorLineAndColumn.mLine + 1);
psi.SetArguments(args);
psi.UseShellExecute = false;
SpawnedProcess process = scope SpawnedProcess();
process.Start(psi).IgnoreError();
/*if (case .Ok(var process) = Process.Start(psi))
delete process;*/
}
}
[IDECommand]
public void Cmd_ZoomReset()
{
SetScale(1.0f, true);
}
2020-03-24 15:26:32 -07:00
[IDECommand]
public void Cmd_QuickInfo()
{
2024-08-27 05:48:22 +02:00
var sourceViewPanel = GetActiveSourceViewPanel(true);
if (sourceViewPanel != null)
{
2020-03-24 15:26:32 -07:00
if (sourceViewPanel.mEditWidget.mEditWidgetContent.GetCursorLineChar(var line, var lineChar))
2021-12-23 07:34:54 -05:00
sourceViewPanel.UpdateMouseover(true, true, line, lineChar, true);
2020-03-24 15:26:32 -07:00
}
}
2019-08-23 11:56:54 -07:00
[IDECommand]
2024-08-27 05:48:22 +02:00
public void Cmd_ReformatDocument()
{
var sourceViewPanel = GetActiveSourceViewPanel(true);
if (sourceViewPanel != null)
sourceViewPanel.ReformatDocument();
}
void RemoveAllBreakpoints()
{
2019-08-23 11:56:54 -07:00
BfLog.LogDbg("IDEApp.RemoveAllBreakpoints\n");
2024-08-27 05:48:22 +02:00
while (mDebugger.mBreakpointList.Count > 0)
mDebugger.DeleteBreakpoint(mDebugger.mBreakpointList[0]);
}
2019-08-23 11:56:54 -07:00
[IDECommand]
2024-08-27 05:48:22 +02:00
void Cmd_Break()
{
mDebugger.BreakAll();
}
public void ShowDisassemblyAtCursor()
{
if (!mDebugger.mIsRunning)
return; // Ignore
var sourceViewPanel = GetActiveSourceViewPanel();
if (sourceViewPanel != null)
{
int line;
int lineChar;
sourceViewPanel.mEditWidget.Content.GetCursorLineChar(out line, out lineChar);
var disassemblyPanel = ShowDisassemblyPanel();
if (!disassemblyPanel.Show(sourceViewPanel.mFilePath, line, lineChar))
ShowRecentFile(1); // Go back a file
}
}
2019-08-23 11:56:54 -07:00
[IDECommand]
2024-08-27 05:48:22 +02:00
public void ShowDisassemblyAtStack()
{
2019-08-23 11:56:54 -07:00
var activePanel = GetActivePanel();
if (var diassemblyPanel = activePanel as DisassemblyPanel)
{
diassemblyPanel.mStayInDisassemblyCheckbox.Checked ^= true;
return;
}
2024-08-27 05:48:22 +02:00
if ((mDebugger.mIsRunning) && (mDebugger.IsPaused()))
{
ShowPCLocation(mDebugger.mActiveCallStackIdx, false, false, true);
}
2019-08-23 11:56:54 -07:00
else
{
ShowDisassemblyPanel(true);
}
mInDisassemblyView = true;
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public void GoToDefinition(bool force)
{
var sourceViewPanel = GetActiveSourceViewPanel(false, true);
if (sourceViewPanel != null)
{
2020-04-07 08:29:54 -07:00
if (!force)
{
if ((!sourceViewPanel.mIsBeefSource) || (sourceViewPanel.mProjectSource == null))
return;
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if ((!sourceViewPanel.mEditWidget.Content.GetCursorLineChar(var line, var lineChar)) && (!force))
2020-04-07 08:29:54 -07:00
return;
2024-08-27 05:48:22 +02:00
if (!sourceViewPanel.HasTextAtCursor())
return;
2019-08-23 11:56:54 -07:00
#if IDE_C_SUPPORT
2024-08-27 05:48:22 +02:00
if (sourceViewPanel.mIsClang)
{
String defFile = scope String();
int defLine;
int defColumn;
mResolveClang.CancelBackground();
2025-03-19 11:01:28 -04:00
2024-08-27 05:48:22 +02:00
int defIdx = sourceViewPanel.mEditWidget.Content.GetTextIdx(line, lineChar);
if (mResolveClang.FindDefinition(sourceViewPanel.mFilePath, defIdx,
defFile, out defLine, out defColumn))
{
sourceViewPanel.RecordHistoryLocation();
sourceViewPanel = ShowSourceFileLocation(defFile, -1, -1, defLine, defColumn, LocatorType.Smart, true);
if (sourceViewPanel != null)
sourceViewPanel.RecordHistoryLocation();
return;
}
}
else
2019-08-23 11:56:54 -07:00
#endif
2025-03-19 11:01:28 -04:00
/*{
2024-08-27 05:48:22 +02:00
ResolveParams resolveParams = scope ResolveParams();
sourceViewPanel.Classify(ResolveType.GoToDefinition, resolveParams);
if (resolveParams.mOutFileName != null)
{
sourceViewPanel.RecordHistoryLocation();
sourceViewPanel = ShowSourceFileLocation(resolveParams.mOutFileName, -1, -1, resolveParams.mOutLine, resolveParams.mOutLineChar, LocatorType.Smart, true);
sourceViewPanel.RecordHistoryLocation(true);
return;
}
}
2019-08-23 11:56:54 -07:00
if (mBfResolveCompiler.HasResolvedAll())
{
2024-08-27 05:48:22 +02:00
Fail("Unable to locate definition");
2019-08-23 11:56:54 -07:00
}
else*/
2019-08-23 11:56:54 -07:00
{
sourceViewPanel.ShowSymbolReferenceHelper(.GoToDefinition);
}
2024-08-27 05:48:22 +02:00
}
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public void StackPositionChanged()
{
2019-08-23 11:56:54 -07:00
WithWatchPanels(scope (watchPanel) =>
{
watchPanel.MarkWatchesDirty(false, true);
});
2024-08-27 05:48:22 +02:00
mMemoryPanel.MarkViewDirty();
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public void RefreshWatches()
{
2019-08-23 11:56:54 -07:00
//Debug.WriteLine("RefreshWatches");
WithWatchPanels(scope (watchPanel) =>
{
watchPanel.MarkWatchesDirty(false);
});
MarkDirty();
for (var window in gApp.mWindows)
{
var widgetWindow = window as WidgetWindow;
if (var hoverWatch = widgetWindow.mRootWidget as HoverWatch)
{
hoverWatch.Refresh();
}
}
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public void MemoryEdited()
{
RefreshWatches();
mMemoryPanel.MarkViewDirty();
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public void AddWatch(String watchExpr)
{
2019-08-23 11:56:54 -07:00
ShowWatches();
2024-08-27 05:48:22 +02:00
mWatchPanel.AddWatchItem(watchExpr);
mWatchPanel.MarkWatchesDirty(false);
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public bool IsInDisassemblyMode(bool wantShowSource = false)
{
2019-08-23 11:56:54 -07:00
//return GetActiveDocumentPanel() is DisassemblyPanel;
2024-08-27 05:48:22 +02:00
if (!mInDisassemblyView)
return false;
DisassemblyPanel disassemblyPanel = TryGetDisassemblyPanel();
if (disassemblyPanel == null)
return false;
return ((disassemblyPanel.mStayInDisassemblyCheckbox != null) && (disassemblyPanel.mStayInDisassemblyCheckbox.Checked)) || (!wantShowSource);
}
DisassemblyPanel TryGetDisassemblyPanel(bool onlyIfVisible = true)
{
Debug.Assert(true);
DisassemblyPanel disassemblyPanel = null;
WithTabs(scope [?] (tabButton) =>
{
if ((disassemblyPanel == null) && (tabButton.mContent is DisassemblyPanel))
{
var checkDisassemblyPanel = (DisassemblyPanel)tabButton.mContent;
if ((!onlyIfVisible) || (checkDisassemblyPanel.mWidgetWindow != null))
disassemblyPanel = checkDisassemblyPanel;
}
});
return disassemblyPanel;
}
2019-08-23 11:56:54 -07:00
2019-08-23 11:56:54 -07:00
[IDECommand]
void Compile()
{
Compile(.Normal);
}
void Compile(CompileKind compileKind)
2019-08-23 11:56:54 -07:00
{
CompilerLog("IDEApp.Compile");
2019-12-01 10:19:00 -08:00
for (let project in gApp.mWorkspace.mProjects)
{
2021-03-02 06:29:53 -08:00
if (project.mDeferState != .None)
{
OutputErrorLine($"Project '{project.mProjectName}' is still loading.");
return;
}
2019-12-01 10:19:00 -08:00
if (project.mFailed)
{
OutputErrorLine("Project '{}' is not loaded. Retry loading by right clicking on the project in the Workspace panel and selecting 'Retry Load'", project.mProjectName);
return;
}
}
2019-08-23 11:56:54 -07:00
if (AreTestsRunning())
return;
if (mHotResolveState != .None)
return;
if (IsCompiling)
return;
2019-08-23 11:56:54 -07:00
if (mWorkspace.mProjects.IsEmpty)
{
Fail("No projects exist to compile. Create or load a project.");
return;
}
if (mWorkspace.IsDebugSession)
{
bool hadCommands = false;
for (let project in mWorkspace.mProjects)
{
if (project.mGeneralOptions.mTargetType == .CustomBuild)
{
let options = GetCurProjectOptions(project);
if (options == null)
continue;
if ((!options.mBuildOptions.mPreBuildCmds.IsEmpty) || (!options.mBuildOptions.mPostBuildCmds.IsEmpty))
hadCommands = true;
}
else
hadCommands = true;
}
if (!hadCommands)
{
Fail("No build commands have been defined");
return;
}
}
if ((!mDebugger.mIsRunning) || (!mDebugger.mIsRunningCompiled))
2024-08-27 05:48:22 +02:00
{
if (mExecutionQueue.Count == 0)
{
mOutputPanel.Clear();
if (compileKind == .DebugComptime)
2024-08-27 05:48:22 +02:00
OutputLine("Compiling with comptime debugging...");
2022-03-08 06:27:06 -08:00
else
OutputLine("Compiling...");
2024-08-27 05:48:22 +02:00
Compile(compileKind, null);
}
2019-08-23 11:56:54 -07:00
}
else if ((mDebugger.mIsRunning) && (!mDebugger.HasLoadedTargetBinary()))
{
Compile(.WhileRunning, null);
}
2019-08-23 11:56:54 -07:00
else
{
2024-08-27 05:48:22 +02:00
mOutputPanel.Clear();
OutputLine("Hot Compiling...");
Project runningProject = mWorkspace.mStartupProject;
Compile(compileKind, runningProject);
2019-08-23 11:56:54 -07:00
}
}
[IDECommand]
void RunWithStep()
{
mTargetStartWithStep = true;
CompileAndRun(true);
}
2019-08-23 11:56:54 -07:00
[IDECommand]
void StepInto()
{
if (mDebugger.mIsRunning)
{
if ((mExecutionPaused) && (mDebugger.IsPaused()))
2024-08-27 05:48:22 +02:00
{
DebuggerUnpaused();
mDebugger.StepInto(IsInDisassemblyMode());
}
2019-08-23 11:56:54 -07:00
}
else
{
2024-08-27 05:48:22 +02:00
RunWithStep();
2019-08-23 11:56:54 -07:00
}
}
[IDECommand]
void StepOver()
{
mStepCount++;
if (mDebugger.mIsRunning)
{
if ((mExecutionPaused) && (mDebugger.IsPaused()))
2024-08-27 05:48:22 +02:00
{
DebuggerUnpaused();
mDebugger.StepOver(IsInDisassemblyMode());
}
2019-08-23 11:56:54 -07:00
}
else
{
if (mEnableRunTiming)
{
mRunTimingProfileId = Profiler.StartSampling("RunTiming");
}
2024-08-27 05:48:22 +02:00
RunWithStep();
2019-08-23 11:56:54 -07:00
}
}
[IDECommand]
void StepOut()
{
if ((mExecutionPaused) && (mDebugger.IsPaused()))
2019-08-23 11:56:54 -07:00
{
2024-08-27 05:48:22 +02:00
DebuggerUnpaused();
mDebugger.StepOut(IsInDisassemblyMode());
2019-08-23 11:56:54 -07:00
}
}
[IDECommand]
void Cmd_Continue()
{
if (mDebugger.mIsRunning)
2024-08-27 05:48:22 +02:00
{
if (mDebugger.IsPaused())
{
DebuggerUnpaused();
mDebugger.Continue();
}
2019-08-23 11:56:54 -07:00
}
}
[IDECommand]
public void RunWithCompiling()
{
if (mDebugger.mIsRunning)
2024-08-27 05:48:22 +02:00
{
if (mDebugger.IsPaused())
{
DebuggerUnpaused();
mDebugger.Continue();
}
2019-08-23 11:56:54 -07:00
}
else if (AreTestsRunning())
{
// Ignore
}
else
{
2024-08-27 05:48:22 +02:00
mTargetStartWithStep = false;
CompileAndRun(true);
2019-08-23 11:56:54 -07:00
}
}
2022-03-08 06:27:06 -08:00
[IDECommand]
public void DebugComptime()
{
if (mDebugger.mIsRunning)
return;
if (IsCompiling)
return;
CheckDebugVisualizers();
Compile(.DebugComptime);
2022-03-08 06:27:06 -08:00
}
2019-08-23 11:56:54 -07:00
[IDECommand]
void RunWithoutCompiling()
{
if (!mDebugger.mIsRunning)
{
2024-08-27 05:48:22 +02:00
OutputLine("Starting target without compiling...");
2019-08-23 11:56:54 -07:00
mTargetStartWithStep = false;
2024-08-27 05:48:22 +02:00
var startDebugCmd = new StartDebugCmd();
2019-08-23 11:56:54 -07:00
startDebugCmd.mWasCompiled = false;
2024-08-27 05:48:22 +02:00
startDebugCmd.mOnlyIfNotFailed = true;
mExecutionQueue.Add(startDebugCmd);
2019-08-23 11:56:54 -07:00
}
}
[IDECommand]
void RunWithoutDebugging()
{
if (mDebugger.mIsRunning)
2024-08-27 05:48:22 +02:00
{
if (mDebugger.IsPaused())
{
DebuggerUnpaused();
mDebugger.Continue();
}
}
else if (AreTestsRunning())
{
// Ignore
}
else
{
2024-08-27 05:48:22 +02:00
mTargetStartWithStep = false;
CompileAndRun(false);
}
}
2019-08-23 11:56:54 -07:00
[IDECommand]
void RunToCursor()
{
var sourceViewPanel = GetActiveSourceViewPanel();
if (mDebugger.mRunToCursorBreakpoint != null)
{
BfLog.LogDbg("Deleting mRunToCursorBreakpoint\n");
mDebugger.DeleteBreakpoint(mDebugger.mRunToCursorBreakpoint);
mDebugger.mRunToCursorBreakpoint = null;
}
if (sourceViewPanel != null)
{
BfLog.LogDbg("Creating mRunToCursorBreakpoint\n");
2019-12-13 14:25:15 -08:00
mDebugger.mRunToCursorBreakpoint = sourceViewPanel.ToggleBreakpointAtCursor(.Force, .None, mDebugger.GetActiveThread());
2019-08-23 11:56:54 -07:00
}
else if (var disassemblyPanel = GetActiveDocumentPanel() as DisassemblyPanel)
{
2019-12-13 14:25:15 -08:00
mDebugger.mRunToCursorBreakpoint = disassemblyPanel.ToggleAddrBreakpointAtCursor(.Force, .None, mDebugger.GetActiveThread());
2019-08-23 11:56:54 -07:00
}
if (mDebugger.mIsRunning)
2024-08-27 05:48:22 +02:00
{
if (mDebugger.IsPaused())
{
DebuggerUnpaused();
mDebugger.Continue();
}
2019-08-23 11:56:54 -07:00
}
else
{
2024-08-27 05:48:22 +02:00
mTargetStartWithStep = false;
CompileAndRun(true);
2019-08-23 11:56:54 -07:00
}
}
[IDECommand]
void SetNextStatement()
{
var documentPanel = GetActiveDocumentPanel();
var sourceViewPanel = documentPanel as SourceViewPanel;
var disassemblyPanel = GetActiveDocumentPanel() as DisassemblyPanel;
if (mDebugger.mIsRunning)
{
2024-08-27 05:48:22 +02:00
if (mExecutionPaused)
{
2019-08-23 11:56:54 -07:00
if (gApp.mDebugger.mActiveCallStackIdx != 0)
{
gApp.Fail("Set Next Statement cannot only be used when the top of the callstack is selected");
return;
}
2024-08-27 05:48:22 +02:00
if (disassemblyPanel != null)
{
String sourceFileName = scope String();
int addr = disassemblyPanel.GetCursorAddress(sourceFileName);
if (addr != (int)0)
{
mDebugger.SetNextStatement(true, sourceFileName, addr, 0);
PCChanged();
}
DebuggerUnpaused();
}
else if (sourceViewPanel != null)
{
2019-08-23 11:56:54 -07:00
var activePanel = sourceViewPanel.GetActivePanel();
2024-08-27 05:48:22 +02:00
int lineIdx;
int lineCharIdx;
var editWidgetContent = activePanel.mEditWidget.Content;
editWidgetContent.GetLineCharAtIdx(editWidgetContent.CursorTextPos, out lineIdx, out lineCharIdx);
2019-08-23 11:56:54 -07:00
/*int hotFileIdx = sourceViewPanel.[Friend]mHotFileIdx;
sourceViewPanel.[Friend]RemapActiveToCompiledLine(hotFileIdx, ref lineIdx, ref lineCharIdx);*/
int textPos = editWidgetContent.CursorTextPos - lineCharIdx;
lineCharIdx = 0;
// Find first non-space char8
while ((textPos < editWidgetContent.mData.mTextLength) && (((char8)editWidgetContent.mData.mText[textPos].mChar).IsWhiteSpace))
{
2024-08-27 05:48:22 +02:00
textPos++;
lineCharIdx++;
2019-08-23 11:56:54 -07:00
}
if (sourceViewPanel.[Friend]mOldVersionPanel == null)
{
int addr;
String file = scope String();
int hotIdx;
int defLineStart;
int defLineEnd;
int line;
int column;
int language;
int stackSize;
mDebugger.CheckCallStack();
String label = scope String();
DebugManager.FrameFlags frameFlags;
mDebugger.GetStackFrameInfo(0, label, out addr, file, out hotIdx, out defLineStart, out defLineEnd, out line, out column, out language, out stackSize, out frameFlags);
if (hotIdx != -1)
sourceViewPanel.[Friend]RemapActiveToCompiledLine(hotIdx, ref lineIdx, ref lineCharIdx);
}
2024-08-27 05:48:22 +02:00
mDebugger.SetNextStatement(false, sourceViewPanel.mFilePath, (int)lineIdx, lineCharIdx);
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
PCChanged();
DebuggerUnpaused();
}
}
2019-08-23 11:56:54 -07:00
}
}
2019-12-13 14:25:15 -08:00
void ToggleBreakpoint(WidgetWindow window, Breakpoint.SetKind setKind, Breakpoint.SetFlags setFlags, bool bindToThread = false)
2019-08-23 11:56:54 -07:00
{
var documentPanel = GetActiveDocumentPanel();
if (var sourceViewPanel = documentPanel as SourceViewPanel)
{
sourceViewPanel = sourceViewPanel.GetFocusedEmbeddedView();
2024-08-27 05:48:22 +02:00
sourceViewPanel.ToggleBreakpointAtCursor(setKind, setFlags, bindToThread ? gApp.mDebugger.GetActiveThread() : -1);
2019-08-23 11:56:54 -07:00
}
else if (var disassemblyPanel = documentPanel as DisassemblyPanel)
2024-08-27 05:48:22 +02:00
{
2019-12-13 14:25:15 -08:00
disassemblyPanel.ToggleBreakpointAtCursor(setKind, setFlags, bindToThread ? gApp.mDebugger.GetActiveThread() : -1);
2019-08-23 11:56:54 -07:00
}
}
[IDECommand]
void ToggleComment()
{
var sourceViewPanel = GetActiveSourceViewPanel();
if (sourceViewPanel == null)
return;
var ewc = (SourceEditWidgetContent)sourceViewPanel.mEditWidget.mEditWidgetContent;
ewc.ToggleComment();
}
[IDECommand]
void ToggleBreakpoint()
{
2019-12-13 14:25:15 -08:00
ToggleBreakpoint(GetCurrentWindow(), .Toggle, .None);
}
[IDECommand]
public void ConfigureBreakpoint()
{
if (var breakpointPanel = GetActivePanel() as BreakpointPanel)
{
breakpointPanel.ConfigureBreakpoints(breakpointPanel.mWidgetWindow);
return;
}
ToggleBreakpoint(GetCurrentWindow(), .EnsureExists, .Configure);
}
[IDECommand]
public void DisableBreakpoint()
{
if (var breakpointPanel = GetActivePanel() as BreakpointPanel)
{
breakpointPanel.SetBreakpointsDisabled(null);
return;
}
ToggleBreakpoint(GetCurrentWindow(), .MustExist, .Disable);
2019-08-23 11:56:54 -07:00
}
[IDECommand]
void ToggleThreadBreakpoint()
{
2019-12-13 14:25:15 -08:00
ToggleBreakpoint(GetCurrentWindow(), .Toggle, .None, true);
2019-08-23 11:56:54 -07:00
}
void CompileCurrentFile()
{
var sourceViewPanel = GetActiveSourceViewPanel();
if (sourceViewPanel == null)
return;
2024-08-27 05:48:22 +02:00
2019-08-23 11:56:54 -07:00
if (sourceViewPanel.mProjectSource == null)
return;
if (!sourceViewPanel.mIsClang)
return;
var project = sourceViewPanel.mProjectSource.mProject;
var options = GetCurProjectOptions(project);
var workspaceOptions = GetCurWorkspaceOptions();
CompileSource(project, workspaceOptions, options, sourceViewPanel.mFilePath, "-v");
}
[IDECommand]
2024-08-27 05:48:22 +02:00
void CancelBuild()
{
mBfBuildCompiler.RequestCancelBackground();
if (IsCompiling)
{
OutputLine("Canceling Compilation...");
2019-08-23 11:56:54 -07:00
//DeleteAndClearItems!(mExecutionQueue);
for (var cmd in mExecutionQueue)
{
#unwarn
if (var processBfCompileCmd = cmd as ProcessBfCompileCmd)
{
}
else if (var buildCompleteCmd = cmd as BuildCompletedCmd)
{
2019-08-23 11:56:54 -07:00
}
else
{
delete cmd;
2019-08-23 11:56:54 -07:00
@cmd.Remove();
}
}
for (var executionInstance in mExecutionInstances)
{
executionInstance.Cancel();
}
2024-08-27 05:48:22 +02:00
}
if ((mBuildContext != null) && (mBuildContext.mScriptManager != null))
mBuildContext.mScriptManager.Cancel();
2024-08-27 05:48:22 +02:00
}
TabbedView FindTabbedView(DockingFrame dockingFrame, int32 xDir, int32 yDir)
{
bool useFirst = true;
if (dockingFrame.mSplitType == DockingFrame.SplitType.Horz)
{
useFirst = xDir > 0;
}
else
useFirst = yDir > 0;
for (int32 pass = 0; pass < 2; pass++)
{
for (int32 i = 0; i < dockingFrame.mDockedWidgets.Count; i++)
{
if ((useFirst) && (i == 0) && (pass == 0))
continue;
var widget = dockingFrame.mDockedWidgets[i];
if (widget is TabbedView)
return (TabbedView)widget;
DockingFrame childFrame = widget as DockingFrame;
if (childFrame != null)
{
TabbedView tabbedView = FindTabbedView(childFrame, xDir, yDir);
if (tabbedView != null)
return tabbedView;
}
}
}
return null;
}
2019-08-23 11:56:54 -07:00
enum ShowTabResult
{
Existing,
OpenedNew
}
2024-08-27 05:48:22 +02:00
ShowTabResult ShowTab(Widget tabContent, String name, bool ownsContent, bool setFocus)
{
2019-08-23 11:56:54 -07:00
var result = ShowTabResult.Existing;
2024-08-27 05:48:22 +02:00
var tabButton = GetTab(tabContent);
if (tabButton == null)
{
2019-08-23 11:56:54 -07:00
TabbedView tabbedView = null;
if (var newPanel = tabContent as Panel)
{
WithTabs(scope [&] (tabButton) =>
2024-08-27 05:48:22 +02:00
{
2019-08-23 11:56:54 -07:00
if (newPanel.HasAffinity(tabButton.mContent))
{
tabbedView = tabButton.mTabbedView;
}
2024-08-27 05:48:22 +02:00
});
2019-08-23 11:56:54 -07:00
}
if (tabbedView == null)
2024-08-27 05:48:22 +02:00
tabbedView = FindTabbedView(mDockingFrame, -1, 1);
2019-08-23 11:56:54 -07:00
if (tabbedView == null)
{
tabbedView = CreateTabbedView();
mDockingFrame.AddDockedWidget(tabbedView, null, .Left);
}
2024-08-27 05:48:22 +02:00
if (tabbedView != null)
2019-08-23 11:56:54 -07:00
{
2024-08-27 05:48:22 +02:00
tabButton = SetupTab(tabbedView, name, 100, tabContent, ownsContent);
2019-08-23 11:56:54 -07:00
result = ShowTabResult.OpenedNew;
}
2024-08-27 05:48:22 +02:00
}
if (tabButton != null)
2019-08-23 11:56:54 -07:00
{
tabButton.RehupScale(1.0f, 1.0f);
2024-08-27 05:48:22 +02:00
tabButton.Activate(setFocus);
2019-08-23 11:56:54 -07:00
}
return result;
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
public void RecordHistoryLocation(bool includeLastActive = false)
2019-08-23 11:56:54 -07:00
{
var sourceViewPanel = GetActiveSourceViewPanel(includeLastActive);
if (sourceViewPanel != null)
2024-08-27 05:48:22 +02:00
sourceViewPanel.RecordHistoryLocation();
2019-08-23 11:56:54 -07:00
}
void ShowPanel(Panel panel, String label, bool setFocus = true)
{
if (!mInitialized)
return;
2020-04-28 12:55:35 -07:00
#if !CLI
if (setFocus)
mLastActivePanel = panel;
2019-08-23 11:56:54 -07:00
RecordHistoryLocation();
ShowTab(panel, label, false, setFocus);
if (setFocus)
panel.FocusForKeyboard();
2020-09-04 08:58:29 -07:00
if ((!panel.mWidgetWindow.mHasFocus) && (!mRunningTestScript))
2020-09-04 08:58:29 -07:00
{
bool hasFocus = false;
BFWindow activeWindow = GetActiveWindow(true);
BFWindow checkWindow = activeWindow;
while (checkWindow != null)
{
if (checkWindow == panel.mWidgetWindow)
{
activeWindow.SetForeground();
hasFocus = true;
break;
}
checkWindow = checkWindow.mParent;
}
2024-08-27 05:48:22 +02:00
2020-09-04 08:58:29 -07:00
if (!hasFocus)
panel.mWidgetWindow.SetForeground();
}
2020-04-28 12:55:35 -07:00
#endif
2019-08-23 11:56:54 -07:00
}
[IDECommand]
public void ShowWorkspacePanel()
{
ShowPanel(mProjectPanel, "Workspace");
}
[IDECommand]
public void ShowClassViewPanel()
{
ShowPanel(mClassViewPanel, "Class View");
}
[IDECommand]
public void ShowThreads()
{
ShowPanel(mThreadPanel, "Threads");
}
[IDECommand]
public void ShowCallstack()
{
2024-08-27 05:48:22 +02:00
ShowPanel(mCallStackPanel, "Call Stack");
2019-08-23 11:56:54 -07:00
}
[IDECommand]
public void ShowErrors()
{
2024-08-27 05:48:22 +02:00
ShowPanel(mErrorsPanel, "Errors");
}
[IDECommand]
public void ShowErrorNext()
{
mErrorsPanel.ShowErrorNext();
}
2019-08-23 11:56:54 -07:00
[IDECommand]
public void ShowWatches()
{
2024-08-27 05:48:22 +02:00
ShowPanel(mWatchPanel, "Watch");
2019-08-23 11:56:54 -07:00
}
[IDECommand]
2024-08-27 05:48:22 +02:00
public void ShowAutoWatches()
{
ShowPanel(mAutoWatchPanel, "Auto Watches");
}
2024-07-19 10:31:33 +02:00
[IDECommand]
public void ShowTerminal()
{
2024-08-27 05:48:22 +02:00
#if BF_PLATFORM_WINDOWS
ShowPanel(mTerminalPanel, "Terminal");
#endif
2024-07-19 10:31:33 +02:00
}
[IDECommand]
public void ShowConsole()
{
2024-08-27 05:48:22 +02:00
#if BF_PLATFORM_WINDOWS
ShowPanel(mConsolePanel, "Console");
#endif
2024-07-19 10:31:33 +02:00
}
2019-08-23 11:56:54 -07:00
[IDECommand]
2024-08-27 05:48:22 +02:00
public void ShowImmediatePanel()
{
ShowPanel(mImmediatePanel, "Immediate");
}
2019-08-23 11:56:54 -07:00
[IDECommand]
2024-08-27 05:48:22 +02:00
public void ShowBreakpoints()
{
ShowPanel(mBreakpointPanel, "Breakpoints");
}
2019-08-23 11:56:54 -07:00
2020-07-18 06:50:28 -07:00
[IDECommand]
public void ShowDiagnostics()
{
ShowPanel(mDiagnosticsPanel, "Diagnostics");
}
2019-08-23 11:56:54 -07:00
[IDECommand]
public void ShowModules()
{
2024-08-27 05:48:22 +02:00
ShowPanel(mModulePanel, "Modules");
2019-08-23 11:56:54 -07:00
}
[IDECommand]
public void ShowMemory()
{
ShowPanel(mMemoryPanel, "Memory");
}
[IDECommand]
public void ShowProfilePanel()
{
2024-08-27 05:48:22 +02:00
ShowPanel(mProfilePanel, "Profile");
2019-08-23 11:56:54 -07:00
}
2022-06-08 20:11:39 +02:00
[IDECommand]
public void ShowBookmarks()
{
2024-08-27 05:48:22 +02:00
ShowPanel(mBookmarksPanel, "Bookmarks");
2022-06-08 20:11:39 +02:00
}
2019-08-23 11:56:54 -07:00
[IDECommand]
public void ShowQuickWatch()
{
QuickWatchDialog dialog = new .();
var activePanel = GetActivePanel();
if (let sourceViewPanel = activePanel as SourceViewPanel)
{
sourceViewPanel.RecordHistoryLocation();
var debugExpr = scope String();
var ewc = sourceViewPanel.EditWidget.Content;
if (ewc.HasSelection())
ewc.GetSelectionText(debugExpr);
else
2025-05-04 07:59:10 +05:00
sourceViewPanel.GetDebugExpressionAt(ewc.mTextCursors.Front.mCursorTextPos, debugExpr);
2019-08-23 11:56:54 -07:00
dialog.Init(debugExpr);
}
else if (let immediatePanel = activePanel as ImmediatePanel)
{
var debugExpr = scope String();
immediatePanel.GetQuickExpression(debugExpr);
dialog.Init(debugExpr);
}
else
{
dialog.Init(.());
}
if (activePanel != null)
dialog.PopupWindow(activePanel.mWidgetWindow);
else
dialog.PopupWindow(mMainWindow);
}
[IDECommand]
public void SelectConfig(String config)
{
mMainFrame.mStatusBar.SelectConfig(config);
}
2019-08-23 11:56:54 -07:00
[IDECommand]
public void SelectConfig()
{
mMainFrame.mStatusBar.mConfigComboBox.ShowDropdown();
}
[IDECommand]
public void SelectPlatform(String platform)
{
mMainFrame.mStatusBar.SelectPlatform(platform);
}
2019-08-23 11:56:54 -07:00
[IDECommand]
public void SelectPlatform()
{
mMainFrame.mStatusBar.mPlatformComboBox.ShowDropdown();
}
[IDECommand]
public void ShowSettings()
{
2024-08-27 05:48:22 +02:00
var workspaceProperties = new SettingsDialog();
workspaceProperties.PopupWindow(mMainWindow);
2019-08-23 11:56:54 -07:00
}
[IDECommand]
public void ReloadSettings()
{
var prevSettings = mSettings;
defer delete prevSettings;
mSettings = new .(prevSettings);
DeleteAndNullify!(mKeyChordState);
2024-08-27 05:48:22 +02:00
mSettings.Load();
mSettings.Apply();
UpdateRecentFileMenuItems();
UpdateRecentDisplayedFilesMenuItems();
}
public void CheckReloadSettings()
{
if (mSettings.WantsReload())
ReloadSettings();
}
[IDECommand]
public void ResetUI()
{
while (mWindows.Count > 1)
mWindows.Back.Close(true);
void ResetPanel(Widget widget)
{
if (widget.mParent != null)
widget.RemoveSelf();
}
if (!mRunningTestScript)
{
mActiveDocumentsTabbedView = null;
WithStandardPanels(scope (panel) =>
{
ResetPanel(panel);
});
mMainFrame.Reset();
}
mDockingFrame = mMainFrame.mDockingFrame;
CreateDefaultLayout(false);
}
[IDECommand]
public void SafeModeToggle()
{
mSafeMode = !mSafeMode;
mNoResolve = mSafeMode;
mWantsBeefClean = true;
}
2019-08-23 11:56:54 -07:00
[IDECommand]
public void ShowKeyboardShortcuts()
{
2024-08-27 05:48:22 +02:00
/*var workspaceProperties = new SettingsDialog();
workspaceProperties.PopupWindow(mMainWindow);*/
2019-08-23 11:56:54 -07:00
}
public void ShowFindResults(bool setFocus)
{
2024-08-27 05:48:22 +02:00
ShowPanel(mFindResultsPanel, "Find Results", setFocus);
2019-08-23 11:56:54 -07:00
}
[IDECommand]
2024-08-27 05:48:22 +02:00
public void ShowFindResults()
{
ShowFindResults(true);
}
2019-08-23 11:56:54 -07:00
[IDECommand]
public void ShowOutput()
{
2024-08-27 05:48:22 +02:00
ShowPanel(mOutputPanel, "Output", false);
2019-08-23 11:56:54 -07:00
}
[IDECommand]
public void ShowAutoCompletePanel()
{
2024-08-27 05:48:22 +02:00
ShowPanel(mAutoCompletePanel, "Autocomplete", false);
2019-08-23 11:56:54 -07:00
}
[IDECommand]
2024-08-27 05:48:22 +02:00
private void OpenCorresponding()
{
var sourceViewPanel = GetActiveSourceViewPanel();
if (sourceViewPanel != null)
{
String fileName = sourceViewPanel.mFilePath;
String findFileName = null;
int dotPos = fileName.LastIndexOf('.');
if ((fileName.EndsWith(".cpp", StringComparison.OrdinalIgnoreCase)) || (fileName.EndsWith(".c", StringComparison.OrdinalIgnoreCase)))
{
findFileName = scope:: String(fileName, 0, dotPos);
findFileName.Append(".h");
}
else if ((fileName.EndsWith(".h", StringComparison.OrdinalIgnoreCase)) || (fileName.EndsWith(".hpp", StringComparison.OrdinalIgnoreCase)))
{
findFileName = scope:: String(fileName, 0, dotPos);
findFileName.Append(".c");
if (!File.Exists(findFileName))
{
findFileName = scope:: String(fileName, 0, dotPos);
findFileName.Append(".cpp");
}
}
if (findFileName != null)
{
if (File.Exists(findFileName))
{
ShowSourceFile(findFileName);
}
else
{
Fail("Unable to find corresponding file");
}
}
}
}
2019-08-23 11:56:54 -07:00
DarkTabbedView GetActiveTabbedView()
{
var activePanel = GetActivePanel();
if (activePanel == null)
return null;
return activePanel.mParent as DarkTabbedView;
}
2021-02-03 12:48:04 -08:00
void SyncWithWorkspacePanel()
{
var activeSourceViewPanel = GetActiveSourceViewPanel();
if (activeSourceViewPanel != null)
activeSourceViewPanel.SyncWithWorkspacePanel();
}
2019-08-23 11:56:54 -07:00
[IDECommand]
void TabFirst()
{
var tabbedView = GetActiveTabbedView();
if ((tabbedView == null) || (tabbedView.mTabs.IsEmpty))
return;
tabbedView.mTabs[0].Activate();
}
[IDECommand]
void TabLast()
{
var tabbedView = GetActiveTabbedView();
if ((tabbedView == null) || (tabbedView.mTabs.IsEmpty))
return;
tabbedView.mTabs.Back.Activate();
}
[IDECommand]
void TabNext()
{
var tabbedView = GetActiveTabbedView();
if ((tabbedView == null) || (tabbedView.mTabs.IsEmpty))
return;
2019-12-13 14:25:15 -08:00
TabbedView.TabButton activateTab = tabbedView.mTabs[0];
2019-08-23 11:56:54 -07:00
for (var tab in tabbedView.mTabs)
{
if (tab.mIsActive)
{
if (@tab.Index < tabbedView.mTabs.Count - 1)
{
2019-12-13 14:25:15 -08:00
activateTab = tabbedView.mTabs[@tab.Index + 1];
break;
2019-08-23 11:56:54 -07:00
}
}
}
2019-12-13 14:25:15 -08:00
activateTab.Activate();
if (var sourceViewPanel = activateTab.mContent as SourceViewPanel)
sourceViewPanel.HilitePosition(.Extra);
2019-08-23 11:56:54 -07:00
}
[IDECommand]
void TabPrev()
{
var tabbedView = GetActiveTabbedView();
if ((tabbedView == null) || (tabbedView.mTabs.IsEmpty))
return;
2019-12-13 14:25:15 -08:00
TabbedView.TabButton activateTab = tabbedView.mTabs.Back;
2019-08-23 11:56:54 -07:00
for (var tab in tabbedView.mTabs)
{
if (tab.mIsActive)
{
if (@tab.Index > 0)
{
2019-12-13 14:25:15 -08:00
activateTab = tabbedView.mTabs[@tab.Index - 1];
break;
2019-08-23 11:56:54 -07:00
}
}
}
2019-12-13 14:25:15 -08:00
activateTab.Activate();
if (var sourceViewPanel = activateTab.mContent as SourceViewPanel)
sourceViewPanel.HilitePosition(.Extra);
2019-08-23 11:56:54 -07:00
}
void DoErrorTest()
{
Dialog aDialog = ThemeFactory.mDefault.CreateDialog("ERROR", "This\nmultiline!\nLine 3.", DarkTheme.sDarkTheme.mIconError);
2024-08-27 05:48:22 +02:00
aDialog.mDefaultButton = aDialog.AddButton("OK");
aDialog.mEscButton = aDialog.mDefaultButton;
aDialog.PopupWindow(GetCurrentWindow());
2019-08-23 11:56:54 -07:00
}
void ReportMemory()
{
mDebugger.FullReportMemory();
if (mBfResolveSystem != null)
2024-08-27 05:48:22 +02:00
mBfResolveSystem.ReportMemory();
mBfBuildSystem.ReportMemory();
Internal.ReportMemory();
GC.Report();
2019-08-23 11:56:54 -07:00
}
[IDECommand]
void DoAttach()
{
var widgetWindow = GetCurrentWindow();
if (widgetWindow != null)
{
2024-08-27 05:48:22 +02:00
var attachDialog = new AttachDialog();
attachDialog.PopupWindow(mMainWindow);
2019-08-23 11:56:54 -07:00
}
}
[IDECommand]
void DoLaunch()
{
if (mLaunchDialog != null)
{
2024-08-27 05:48:22 +02:00
mLaunchDialog.mWidgetWindow.SetForeground();
return;
2019-08-23 11:56:54 -07:00
}
mLaunchDialog = new LaunchDialog();
mLaunchDialog.PopupWindow(mMainWindow);
mLaunchDialog.mOnClosed.Add(new () => { mLaunchDialog = null; });
}
2024-08-27 05:48:22 +02:00
2019-08-23 11:56:54 -07:00
void DoProfile()
{
if (gApp.mProfilePanel.mUserProfiler != null)
{
ShowProfilePanel();
gApp.mProfilePanel.StopProfiling();
}
else
{
ShowProfilePanel();
gApp.mProfilePanel.StartProfiling();
}
}
[IDECommand]
void NavigateBackwards()
{
var sourceViewPanel = GetActiveSourceViewPanel();
if ((sourceViewPanel != null) && (sourceViewPanel.HasFocus()))
{
2022-03-01 06:11:28 -08:00
if ((sourceViewPanel?.mQuickFind?.mIsShowingMatches == true) && (sourceViewPanel.mQuickFind.mFindEditWidget.mHasFocus))
{
sourceViewPanel.SetFocus();
return;
}
2019-08-23 11:56:54 -07:00
var sourceEditWidgetContent = (SourceEditWidgetContent)sourceViewPanel.mEditWidget.mEditWidgetContent;
if (sourceEditWidgetContent.IsAtCurrentHistory())
{
mHistoryManager.PrevHistory();
return;
}
}
mHistoryManager.GoToCurrentHistory();
}
[IDECommand]
void NavigateForwards()
{
mHistoryManager.NextHistory();
}
void ScopePrev()
{
var sewc = GetActiveSourceEditWidgetContent();
if (sewc != null)
sewc.ScopePrev();
}
void ScopeNext()
{
var sewc = GetActiveSourceEditWidgetContent();
if (sewc != null)
sewc.ScopeNext();
}
2022-01-07 20:24:23 +01:00
void ScrollDown()
{
var sewc = GetActiveSourceEditWidgetContent();
if (sewc != null)
{
var scrollbar = sewc.mEditWidget.mVertScrollbar;
if (scrollbar != null)
scrollbar.Scroll(+1 * scrollbar.GetScrollIncrement());
}
}
void ScrollUp()
{
var sewc = GetActiveSourceEditWidgetContent();
if (sewc != null)
{
var scrollbar = sewc.mEditWidget.mVertScrollbar;
if (scrollbar != null)
scrollbar.Scroll(-1 * scrollbar.GetScrollIncrement());
}
}
2019-08-23 11:56:54 -07:00
void ExitTest()
{
sExitTest = true;
Stop();
}
void ToggleCheck(IMenu menu, ref bool checkVal)
{
checkVal = !checkVal;
var sysMenu = (SysMenu)menu;
sysMenu.Modify(null, null, null, true, checkVal ? 1 : 0);
}
public Menu AddMenuItem(Menu menu, StringView label, StringView command = default)
{
var command;
if (command.IsEmpty)
command = label;
String labelStr = scope String(label);
if (mCommands.mCommandMap.TryGetAlt(command, var matchKey, var ideCommand))
{
labelStr.Append("|");
ideCommand.ToString(labelStr);
}
return menu.AddItem(labelStr);
}
2019-08-23 11:56:54 -07:00
public bool AreTestsRunning()
{
return (mTestManager != null);
}
[IDECommand]
2019-12-13 14:25:15 -08:00
protected void RunTests(bool includeIgnored, bool debug)
2019-08-23 11:56:54 -07:00
{
2024-10-25 07:41:53 -04:00
var workspaceOptions = GetCurWorkspaceOptions();
if (CurrentPlatform == .Wasm)
{
if (workspaceOptions.mBuildKind != .Test)
mMainFrame.mStatusBar.SelectConfig("Test");
CompileAndRun(true);
return;
}
2019-08-23 11:56:54 -07:00
if (mOutputPanel != null)
{
ShowPanel(mOutputPanel, false);
mOutputPanel.Clear();
}
if (AreTestsRunning())
{
2019-12-13 14:25:15 -08:00
OutputErrorLine("Tests already running");
2019-08-23 11:56:54 -07:00
return;
}
if ((mDebugger != null) && (mDebugger.mIsRunning))
{
2019-12-13 14:25:15 -08:00
OutputErrorLine("Tests cannot be run while program is executing");
2019-08-23 11:56:54 -07:00
return;
}
String prevConfigName = scope String(mConfigName);
if (workspaceOptions.mBuildKind != .Test)
{
mMainFrame.mStatusBar.SelectConfig("Test");
}
workspaceOptions = GetCurWorkspaceOptions();
if (workspaceOptions.mBuildKind != .Test)
{
mMainFrame.mStatusBar.SelectConfig(prevConfigName);
2019-12-13 14:25:15 -08:00
OutputErrorLine("No valid Test workspace configuration exists");
2019-08-23 11:56:54 -07:00
return;
}
2024-10-25 07:41:53 -04:00
var platformType = Workspace.PlatformType.GetFromName(gApp.mPlatformName, workspaceOptions.mTargetTriple);
2019-08-23 11:56:54 -07:00
mLastTestFailed = false;
mTestManager = new TestManager();
mTestManager.mPrevConfigName = new String(prevConfigName);
2024-10-25 07:41:53 -04:00
mTestManager.mDebug = debug && (platformType != .Wasm);
2019-12-13 14:25:15 -08:00
mTestManager.mIncludeIgnored = includeIgnored;
2019-08-23 11:56:54 -07:00
if (mOutputPanel != null)
mOutputPanel.Clear();
OutputLine("Compiling for testing...");
if (!Compile(.Test, null))
{
mTestManager.BuildFailed();
}
2019-12-13 14:25:15 -08:00
if (!mTestManager.HasProjects)
{
OutputLineSmart("WARNING: No projects have a test configuration specified");
}
2019-08-23 11:56:54 -07:00
}
[IDECommand]
public void Cmd_TestEnableConsole()
{
let ideCommand = gApp.mCommands.mCommandMap["Test Enable Console"];
ToggleCheck(ideCommand.mMenuItem, ref mTestEnableConsole);
}
2025-05-04 07:59:10 +05:00
[IDECommand]
2025-05-26 08:25:07 +02:00
public void Cmd_AddSelectionToNextFindMatch()
2025-05-04 07:59:10 +05:00
{
GetActiveSourceViewPanel()?.AddSelectionToNextFindMatch();
2025-05-04 07:59:10 +05:00
}
[IDECommand]
2025-05-26 08:25:07 +02:00
public void Cmd_MoveLastSelectionToNextFindMatch()
2025-05-04 07:59:10 +05:00
{
GetActiveSourceViewPanel()?.MoveLastSelectionToNextFindMatch();
}
[IDECommand]
public void Cmd_AddCursorAbove()
{
GetActiveSourceEditWidgetContent()?.AddMultiCursor(-1);
}
[IDECommand]
public void Cmd_AddCursorBelow()
{
GetActiveSourceEditWidgetContent()?.AddMultiCursor(1);
2025-05-04 07:59:10 +05:00
}
2020-04-30 10:34:37 -07:00
public void UpdateMenuItem_HasActivePanel(IMenu menu)
{
menu.SetDisabled(GetActivePanel() == null);
}
public void UpdateMenuItem_HasActiveDocument(IMenu menu)
{
menu.SetDisabled(GetActiveDocumentPanel() == null);
}
public void UpdateMenuItem_HasLastActiveDocument(IMenu menu)
{
menu.SetDisabled(GetLastActiveDocumentPanel() == null);
}
2020-04-30 10:34:37 -07:00
public void UpdateMenuItem_HasWorkspace(IMenu menu)
{
menu.SetDisabled(!gApp.mWorkspace.IsInitialized);
}
public void UpdateMenuItem_DebugPaused(IMenu menu)
{
menu.SetDisabled(!mDebugger.mIsRunning || !mExecutionPaused);
}
public void UpdateMenuItem_DebugPausedOrStopped_HasWorkspace(IMenu menu)
{
if (mDebugger.mIsRunning)
menu.SetDisabled(!mExecutionPaused);
else
menu.SetDisabled(!mWorkspace.IsInitialized);
}
public void UpdateMenuItem_DebugNotPaused(IMenu menu)
{
menu.SetDisabled(!mDebugger.mIsRunning || mExecutionPaused);
}
public void UpdateMenuItem_DebugRunning(IMenu menu)
{
menu.SetDisabled(!mDebugger.mIsRunning);
}
public void UpdateMenuItem_DebugOrTestRunning(IMenu menu)
{
menu.SetDisabled(!mDebugger.mIsRunning && (mTestManager == null));
}
2020-04-30 10:34:37 -07:00
public void UpdateMenuItem_DebugStopped_HasWorkspace(IMenu menu)
{
menu.SetDisabled(mDebugger.mIsRunning || !mWorkspace.IsInitialized);
}
public void UpdateMenuItem_DebugStopped(IMenu menu)
{
menu.SetDisabled(mDebugger.mIsRunning);
}
2024-08-27 05:48:22 +02:00
public void CreateMenu()
{
2019-08-23 11:56:54 -07:00
scope AutoBeefPerf("IDEApp.CreateMenu");
2024-08-27 05:48:22 +02:00
SysMenu root = mMainWindow.mSysMenu;
2019-08-23 11:56:54 -07:00
String keyStr = scope String();
2024-08-27 05:48:22 +02:00
2019-08-23 11:56:54 -07:00
SysMenu AddMenuItem(SysMenu menu, String dispString, String cmdName, MenuItemUpdateHandler menuItemUpdateHandler = null,
2024-08-27 05:48:22 +02:00
SysBitmap bitmap = null, bool enabled = true, int32 checkState = -1, bool radioCheck = false)
2019-08-23 11:56:54 -07:00
{
let ideCommand = mCommands.mCommandMap[cmdName];
if (ideCommand != null)
{
keyStr.Clear();
ideCommand.ToString(keyStr);
keyStr.Insert(0, "#");
}
let itemMenu = menu.AddMenuItem(dispString, (ideCommand != null) ? keyStr : null, new (evt) => ideCommand.mAction(), menuItemUpdateHandler, bitmap, enabled, checkState, radioCheck);
if (ideCommand != null)
{
ideCommand.mMenuItem = itemMenu;
}
return itemMenu;
}
//////////
2024-08-27 05:48:22 +02:00
SysMenu subMenu = root.AddMenuItem("&File");
2019-08-23 11:56:54 -07:00
let newMenu = subMenu.AddMenuItem("&New");
AddMenuItem(newMenu, "New &Workspace", "New Workspace");
2024-08-27 05:48:22 +02:00
AddMenuItem(newMenu, "New &Project", "New Project");
2019-08-23 11:56:54 -07:00
AddMenuItem(newMenu, "New &Debug Session", "New Debug Session");
AddMenuItem(newMenu, "New &File", "New File");
2024-08-27 05:48:22 +02:00
let openMenu = subMenu.AddMenuItem("&Open");
2019-08-23 11:56:54 -07:00
//openMenu.AddMenuItem("&Open Workspace...", GetCmdKey("Open Workspace"), new (evt) => { OpenWorkspace(); } );
AddMenuItem(openMenu, "Open &Workspace...", "Open Workspace");
AddMenuItem(openMenu, "Open &Project...", "Open Project");
AddMenuItem(openMenu, "Open &Debug Session...", "Open Debug Session");
AddMenuItem(openMenu, "Open &File...", "Open File");
AddMenuItem(openMenu, "&Open File in Workspace", "Open File in Workspace");
AddMenuItem(openMenu, "&Open Corresponding (cpp/h)", "Open Corresponding");
AddMenuItem(openMenu, "Open &Crash Dump...", "Open Crash Dump");
let recentMenu = subMenu.AddMenuItem("Open &Recent");
mSettings.mRecentFiles.mRecents[(int)RecentFiles.RecentKind.OpenedWorkspace].mMenu = recentMenu.AddMenuItem("Open Recent &Workspace");
mSettings.mRecentFiles.mRecents[(int)RecentFiles.RecentKind.OpenedDebugSession].mMenu = recentMenu.AddMenuItem("Open Recent &Debug Session");
mSettings.mRecentFiles.mRecents[(int)RecentFiles.RecentKind.OpenedFile].mMenu = recentMenu.AddMenuItem("Open Recent &File");
mSettings.mRecentFiles.mRecents[(int)RecentFiles.RecentKind.OpenedCrashDump].mMenu = recentMenu.AddMenuItem("Open Recent &Crash Dump");
2024-08-27 05:48:22 +02:00
AddMenuItem(subMenu, "&Save File", "Save File", new => UpdateMenuItem_HasActiveDocument);
AddMenuItem(subMenu, "Save &As...", "Save As", new => UpdateMenuItem_HasActiveDocument);
2019-08-23 11:56:54 -07:00
AddMenuItem(subMenu, "Save A&ll", "Save All");
let prefMenu = subMenu.AddMenuItem("&Preferences");
AddMenuItem(prefMenu, "&Settings", "Settings");
AddMenuItem(prefMenu, "Reload Settings", "Reload Settings");
AddMenuItem(prefMenu, "Reset UI", "Reset UI");
AddMenuItem(prefMenu, "Safe Mode", "Safe Mode Toggle", new (menu) => { menu.SetCheckState(mSafeMode ? 1 : 0); }, null, true, mSafeMode ? 1 : 0);
2020-04-30 10:34:37 -07:00
AddMenuItem(subMenu, "Close Workspace", "Close Workspace", new => UpdateMenuItem_HasWorkspace);
2024-08-27 05:48:22 +02:00
AddMenuItem(subMenu, "E&xit", "Exit");
2019-08-23 11:56:54 -07:00
//////////
2024-08-27 05:48:22 +02:00
subMenu = root.AddMenuItem("&Edit");
AddMenuItem(subMenu, "Quick &Find...", "Find in Document", new => UpdateMenuItem_HasActivePanel);
AddMenuItem(subMenu, "Quick &Replace...", "Replace in Document", new => UpdateMenuItem_HasActiveDocument);
AddMenuItem(subMenu, "Find in &Files...", "Find in Files");
AddMenuItem(subMenu, "Replace in Files...", "Replace in Files");
2020-04-30 10:34:37 -07:00
AddMenuItem(subMenu, "Find Prev", "Find Prev", new => UpdateMenuItem_HasActivePanel);
2024-08-27 05:48:22 +02:00
AddMenuItem(subMenu, "Find Next", "Find Next", new => UpdateMenuItem_HasActivePanel);
2019-08-23 11:56:54 -07:00
AddMenuItem(subMenu, "Show &Current", "Show Current");
2024-08-27 05:48:22 +02:00
AddMenuItem(subMenu, "&Goto Line...", "Goto Line", new => UpdateMenuItem_HasActiveDocument);
AddMenuItem(subMenu, "Goto &Method...", "Goto Method", new => UpdateMenuItem_HasActiveDocument);
AddMenuItem(subMenu, "&Rename Symbol", "Rename Symbol", new => UpdateMenuItem_HasActiveDocument);
2020-04-30 10:34:37 -07:00
AddMenuItem(subMenu, "Show Fi&xit", "Show Fixit", new => UpdateMenuItem_HasActiveDocument);
AddMenuItem(subMenu, "Find &All References", "Find All References", new => UpdateMenuItem_HasActiveDocument);
2019-08-23 11:56:54 -07:00
AddMenuItem(subMenu, "Find C&lass...", "Find Class");
subMenu.AddMenuItem(null);
var encodingMenu = subMenu.AddMenuItem("Encoding");
var lineEndingMenu = encodingMenu.AddMenuItem("Line Ending");
void AddLineEndingKind(String name, LineEndingKind lineEndingKind)
{
lineEndingMenu.AddMenuItem(name, null,
new (menu) =>
{
var sysMenu = (SysMenu)menu;
var sourceViewPanel = GetActiveSourceViewPanel();
if (sourceViewPanel != null)
{
if (sourceViewPanel.mEditData.mLineEndingKind != lineEndingKind)
{
sourceViewPanel.EditWidget.Content.mData.mCurTextVersionId++;
sourceViewPanel.mEditData.mLineEndingKind = lineEndingKind;
sysMenu.mParent.UpdateChildItems();
}
}
},
new (menu) =>
{
var sysMenu = (SysMenu)menu;
var sourceViewPanel = GetActiveSourceViewPanel();
if (sourceViewPanel != null)
{
sysMenu.Modify(null, null, null, true, (sourceViewPanel.mEditData.mLineEndingKind == lineEndingKind) ? 1 : 0, true);
}
else
{
sysMenu.Modify(null, null, null, false, 0, true);
}
});
}
AddLineEndingKind("Windows", .CrLf);
AddLineEndingKind("Unix", .Lf);
AddLineEndingKind("Mac OS 9", .Cr);
var bookmarkMenu = subMenu.AddMenuItem("Boo&kmarks");
AddMenuItem(bookmarkMenu, "&Toggle Bookmark", "Bookmark Toggle");
AddMenuItem(bookmarkMenu, "&Next Bookmark", "Bookmark Next");
AddMenuItem(bookmarkMenu, "&Previous Bookmark", "Bookmark Prev");
AddMenuItem(bookmarkMenu, "&Clear Bookmarks", "Bookmark Clear");
var comptimeMenu = subMenu.AddMenuItem("Comptime");
var emitViewCompiler = comptimeMenu.AddMenuItem("Emit View Compiler");
var subItem = emitViewCompiler.AddMenuItem("Resolve", null,
2024-08-27 05:48:22 +02:00
new (menu) => { SetEmbedCompiler(.Resolve); },
new (menu) => { menu.SetCheckState((mSettings.mEditorSettings.mEmitCompiler == .Resolve) ? 1 : 0); },
null, true, (mSettings.mEditorSettings.mEmitCompiler == .Resolve) ? 1 : 0);
subItem = emitViewCompiler.AddMenuItem("Build", null,
2024-08-27 05:48:22 +02:00
new (menu) => { SetEmbedCompiler(.Build); },
new (menu) => { menu.SetCheckState((mSettings.mEditorSettings.mEmitCompiler == .Build) ? 1 : 0); },
null, true, (mSettings.mEditorSettings.mEmitCompiler == .Build) ? 1 : 0);
2024-08-27 05:48:22 +02:00
2019-08-23 11:56:54 -07:00
var advancedEditMenu = subMenu.AddMenuItem("Advanced");
AddMenuItem(advancedEditMenu, "Duplicate Line", "Duplicate Line");
AddMenuItem(advancedEditMenu, "Move Line Up", "Move Line Up");
AddMenuItem(advancedEditMenu, "Move Line Down", "Move Line Down");
AddMenuItem(advancedEditMenu, "Move Statement Up", "Move Statement Up");
AddMenuItem(advancedEditMenu, "Move Statement Down", "Move Statement Down");
advancedEditMenu.AddMenuItem(null);
2019-08-23 11:56:54 -07:00
AddMenuItem(advancedEditMenu, "Make Uppercase", "Make Uppercase");
AddMenuItem(advancedEditMenu, "Make Lowercase", "Make Lowercase");
2021-12-18 20:36:46 +02:00
AddMenuItem(advancedEditMenu, "Comment Block", "Comment Block");
AddMenuItem(advancedEditMenu, "Comment Lines", "Comment Lines");
2021-12-21 07:49:57 -05:00
AddMenuItem(advancedEditMenu, "Comment Toggle", "Comment Toggle");
AddMenuItem(advancedEditMenu, "Uncomment Selection", "Uncomment Selection");
2019-08-23 11:56:54 -07:00
AddMenuItem(advancedEditMenu, "Reformat Document", "Reformat Document");
mViewWhiteSpace.mMenu = AddMenuItem(advancedEditMenu, "View White Space", "View White Space", null, null, true, mViewWhiteSpace.Bool ? 1 : 0);
2019-08-23 11:56:54 -07:00
if (mSettings.mEnableDevMode)
{
subMenu.AddMenuItem(null);
var internalEditMenu = subMenu.AddMenuItem("Internal");
internalEditMenu.AddMenuItem("Hilight Cursor References", null, new (menu) => { ToggleCheck(menu, ref gApp.mSettings.mEditorSettings.mHiliteCursorReferences); }, null, null, true, gApp.mSettings.mEditorSettings.mHiliteCursorReferences ? 1 : 0);
internalEditMenu.AddMenuItem("Delayed Autocomplete", null, new (menu) => { ToggleCheck(menu, ref gApp.mDbgDelayedAutocomplete); }, null, null, true, gApp.mDbgDelayedAutocomplete ? 1 : 0);
internalEditMenu.AddMenuItem("Time Autocomplete", null, new (menu) => { ToggleCheck(menu, ref gApp.mDbgTimeAutocomplete); }, null, null, true, gApp.mDbgTimeAutocomplete ? 1 : 0);
internalEditMenu.AddMenuItem("Perf Autocomplete", null, new (menu) => { ToggleCheck(menu, ref gApp.mDbgPerfAutocomplete); }, null, null, true, gApp.mDbgPerfAutocomplete ? 1 : 0);
2024-04-27 08:24:55 -04:00
internalEditMenu.AddMenuItem("Dump Undo Buffer", null, new (menu) =>
{
if (var panel = GetActiveSourceViewPanel())
{
var str = panel.mEditWidget.mEditWidgetContent.mData.mUndoManager.ToString(.. scope .());
Debug.WriteLine(str);
}
}, null, null, true, gApp.mDbgPerfAutocomplete ? 1 : 0);
}
2019-08-23 11:56:54 -07:00
//////////
2024-08-27 05:48:22 +02:00
subMenu = root.AddMenuItem("&View");
2020-07-18 06:50:28 -07:00
AddMenuItem(subMenu, "AutoComplet&e", "Show Autocomplete Panel");
AddMenuItem(subMenu, "&Auto Watches", "Show Auto Watches");
2022-06-08 20:11:39 +02:00
AddMenuItem(subMenu, "Boo&kmarks", "Show Bookmarks");
AddMenuItem(subMenu, "&Breakpoints", "Show Breakpoints");
2019-08-23 11:56:54 -07:00
AddMenuItem(subMenu, "&Call Stack", "Show Call Stack");
AddMenuItem(subMenu, "C&lass View", "Show Class View");
2020-07-18 06:50:28 -07:00
AddMenuItem(subMenu, "&Diagnostics", "Show Diagnostics");
AddMenuItem(subMenu, "E&rrors", "Show Errors");
AddMenuItem(subMenu, "&Find Results", "Show Find Results");
2024-07-19 10:31:33 +02:00
AddMenuItem(subMenu, "&Terminal", "Show Terminal");
AddMenuItem(subMenu, "Co&nsole", "Show Console");
AddMenuItem(subMenu, "&Immediate Window", "Show Immediate");
2019-08-23 11:56:54 -07:00
AddMenuItem(subMenu, "&Memory", "Show Memory");
2020-07-18 06:50:28 -07:00
AddMenuItem(subMenu, "Mod&ules", "Show Modules");
2019-08-23 11:56:54 -07:00
AddMenuItem(subMenu, "&Output", "Show Output");
AddMenuItem(subMenu, "&Profiler", "Show Profiler");
2024-07-19 10:31:33 +02:00
AddMenuItem(subMenu, "T&hreads", "Show Threads");
AddMenuItem(subMenu, "&Watches", "Show Watches");
AddMenuItem(subMenu, "Work&space Explorer", "Show Workspace Explorer");
2019-08-23 11:56:54 -07:00
subMenu.AddMenuItem(null);
AddMenuItem(subMenu, "Next Document Panel", "Next Document Panel");
AddMenuItem(subMenu, "Navigate Backwards", "Navigate Backwards");
AddMenuItem(subMenu, "Navigate Forwards", "Navigate Forwards");
//////////
2024-08-27 05:48:22 +02:00
subMenu = root.AddMenuItem("&Build");
AddMenuItem(subMenu, "&Build Workspace", "Build Workspace", new => UpdateMenuItem_HasWorkspace);
2022-03-15 16:33:30 -07:00
AddMenuItem(subMenu, "&Debug Comptime", "Debug Comptime", new => UpdateMenuItem_DebugStopped_HasWorkspace);
2024-08-27 05:48:22 +02:00
AddMenuItem(subMenu, "&Clean", "Clean", new => UpdateMenuItem_DebugStopped_HasWorkspace);
AddMenuItem(subMenu, "Clean Beef", "Clean Beef", new => UpdateMenuItem_DebugStopped_HasWorkspace);
2019-08-23 11:56:54 -07:00
//subMenu.AddMenuItem("Compile Current File", null, new (menu) => { CompileCurrentFile(); });
2024-08-27 05:48:22 +02:00
AddMenuItem(subMenu, "Cancel Build", "Cancel Build", new (menu) => { menu.SetDisabled(!IsCompiling); });
2019-08-23 11:56:54 -07:00
2023-07-27 07:14:49 -07:00
subMenu.AddMenuItem("Verbose", null, new (menu) =>
{
if (mVerbosity != .Diagnostic)
mVerbosity = .Diagnostic;
else
mVerbosity = .Normal;
var sysMenu = (SysMenu)menu;
sysMenu.Modify(null, null, null, true, (mVerbosity == .Diagnostic) ? 1 : 0);
}, null, null, true, (mVerbosity == .Diagnostic) ? 1 : 0);
if (mSettings.mEnableDevMode)
{
var internalBuildMenu = subMenu.AddMenuItem("Internal");
2024-08-27 05:48:22 +02:00
internalBuildMenu.AddMenuItem("Autobuild (Debug)", null, new (menu) => { mDebugAutoBuild = !mDebugAutoBuild; });
internalBuildMenu.AddMenuItem("Autorun (Debug)", null, new (menu) => { mDebugAutoRun = !mDebugAutoRun; });
internalBuildMenu.AddMenuItem("Disable Compiling", null, new (menu) => { ToggleCheck(menu, ref mDisableBuilding); }, null, null, true, mDisableBuilding ? 1 : 0);
}
2019-08-23 11:56:54 -07:00
//////////
2024-08-27 05:48:22 +02:00
subMenu = root.AddMenuItem("&Debug");
AddMenuItem(subMenu, "&Start Debugging", "Start Debugging", new (item) =>
{
2024-08-27 05:48:22 +02:00
SysMenu sysMenu = (.)item;
if (mDebugger.mIsRunning)
sysMenu.Modify("&Continue", sysMenu.mHotKey, null, mDebugger.IsPaused());
else
sysMenu.Modify("&Start Debugging", sysMenu.mHotKey, null, mWorkspace.IsInitialized);
});
2020-04-30 10:34:37 -07:00
AddMenuItem(subMenu, "Start Wit&hout Debugging", "Start Without Debugging", new => UpdateMenuItem_DebugStopped_HasWorkspace);
AddMenuItem(subMenu, "Start With&out Compiling", "Start Without Compiling", new => UpdateMenuItem_DebugStopped_HasWorkspace);
2020-04-30 10:34:37 -07:00
AddMenuItem(subMenu, "&Launch Process...", "Launch Process", new => UpdateMenuItem_DebugStopped);
AddMenuItem(subMenu, "&Attach to Process...", "Attach to Process", new => UpdateMenuItem_DebugStopped);
AddMenuItem(subMenu, "&Stop Debugging", "Stop Debugging", new => UpdateMenuItem_DebugOrTestRunning);
2024-08-27 05:48:22 +02:00
AddMenuItem(subMenu, "Break All", "Break All", new => UpdateMenuItem_DebugNotPaused);
AddMenuItem(subMenu, "Remove All Breakpoints", "Remove All Breakpoints");
AddMenuItem(subMenu, "Show &Disassembly", "Show Disassembly");
2020-04-30 10:34:37 -07:00
AddMenuItem(subMenu, "&Quick Watch", "Show QuickWatch", new => UpdateMenuItem_DebugPaused);
AddMenuItem(subMenu, "&Profile", "Profile", new => UpdateMenuItem_HasWorkspace);
2019-08-23 11:56:54 -07:00
subMenu.AddMenuItem(null);
2020-04-30 10:34:37 -07:00
AddMenuItem(subMenu, "Step Into", "Step Into", new => UpdateMenuItem_DebugPausedOrStopped_HasWorkspace);
AddMenuItem(subMenu, "Step Over", "Step Over", new => UpdateMenuItem_DebugPausedOrStopped_HasWorkspace);
AddMenuItem(subMenu, "Step Out", "Step Out", new => UpdateMenuItem_DebugPaused);
2019-08-23 11:56:54 -07:00
subMenu.AddMenuItem(null);
2020-04-30 10:34:37 -07:00
AddMenuItem(subMenu, "To&ggle Breakpoint", "Breakpoint Toggle", new => UpdateMenuItem_HasActiveDocument);
AddMenuItem(subMenu, "Toggle Thread Breakpoint", "Breakpoint Toggle Thread", new => UpdateMenuItem_HasActiveDocument);
2019-08-23 11:56:54 -07:00
var newBreakpointMenu = subMenu.AddMenuItem("New &Breakpoint");
2020-04-30 10:34:37 -07:00
AddMenuItem(newBreakpointMenu, "&Memory Breakpoint...", "Breakpoint Memory", new => UpdateMenuItem_DebugRunning);
2019-08-23 11:56:54 -07:00
AddMenuItem(newBreakpointMenu, "&Symbol Breakpoint...", "Breakpoint Symbol");
if (mSettings.mEnableDevMode)
{
var internalDebugMenu = subMenu.AddMenuItem("Internal");
2024-08-27 05:48:22 +02:00
internalDebugMenu.AddMenuItem("Error Test", null, new (menu) => { DoErrorTest(); });
internalDebugMenu.AddMenuItem("Reconnect BeefPerf", null, new (menu) => { BeefPerf.RetryConnect(); });
AddMenuItem(internalDebugMenu, "Report Memory", "Report Memory");
internalDebugMenu.AddMenuItem("Crash", null, new (menu) => { int* ptr = null; *ptr = 123; });
2019-09-23 11:50:11 -07:00
internalDebugMenu.AddMenuItem("Show Welcome", null, new (menu) => { ShowWelcome(); });
internalDebugMenu.AddMenuItem("Exit Test", null, new (menu) => { ExitTest(); });
internalDebugMenu.AddMenuItem("Run Test", null, new (menu) => { mRunTest = !mRunTest; });
internalDebugMenu.AddMenuItem("GC Collect", null, new (menu) =>
2024-08-27 05:48:22 +02:00
{
var profileId = Profiler.StartSampling().GetValueOrDefault();
for (int i < 10)
GC.Collect(false);
if (profileId != 0)
profileId.Dispose();
2024-08-27 05:48:22 +02:00
});
internalDebugMenu.AddMenuItem("Enable GC Collect", null, new (menu) => { ToggleCheck(menu, ref mEnableGCCollect); EnableGCCollect = mEnableGCCollect; }, null, null, true, mEnableGCCollect ? 1 : 0);
internalDebugMenu.AddMenuItem("Fast Updating", null, new (menu) => { ToggleCheck(menu, ref mDbgFastUpdate); EnableGCCollect = mDbgFastUpdate; }, null, null, true, mDbgFastUpdate ? 1 : 0);
internalDebugMenu.AddMenuItem("Alloc String", null, new (menu) => { new String("Alloc String"); });
internalDebugMenu.AddMenuItem("Perform Long Update Checks", null, new (menu) =>
2024-08-27 05:48:22 +02:00
{
bool wantsLongUpdateCheck = mLongUpdateProfileId != 0;
2024-08-27 05:48:22 +02:00
ToggleCheck(menu, ref wantsLongUpdateCheck);
mLastLongUpdateCheck = 0;
mLastLongUpdateCheckError = 0;
if (wantsLongUpdateCheck)
mLongUpdateProfileId = Profiler.StartSampling("LongUpdate");
else
{
mLongUpdateProfileId.Dispose();
mLongUpdateProfileId = 0;
}
2024-08-27 05:48:22 +02:00
}, null, null, true, (mLongUpdateProfileId != 0) ? 1 : 0);
}
2019-08-23 11:56:54 -07:00
//////////
var testMenu = root.AddMenuItem("&Test");
var testRunMenu = testMenu.AddMenuItem("&Run", null, null, new => UpdateMenuItem_DebugStopped_HasWorkspace);
2019-12-13 14:25:15 -08:00
AddMenuItem(testRunMenu, "&Normal Tests", "Run Normal Tests");
2019-08-23 11:56:54 -07:00
AddMenuItem(testRunMenu, "&All Tests", "Run All Tests");
2024-08-27 05:48:22 +02:00
2019-08-23 11:56:54 -07:00
var testDebugMenu = testMenu.AddMenuItem("&Debug", null, null, new => UpdateMenuItem_DebugStopped_HasWorkspace);
2019-12-13 14:25:15 -08:00
AddMenuItem(testDebugMenu, "&Normal Tests", "Debug Normal Tests");
2019-08-23 11:56:54 -07:00
AddMenuItem(testDebugMenu, "&All Tests", "Debug All Tests");
testDebugMenu.AddMenuItem(null);
testDebugMenu.AddMenuItem("Break on Failure", null, new (menu) =>
{
2024-08-27 05:48:22 +02:00
ToggleCheck(menu, ref mTestBreakOnFailure);
}, null, null, true, mTestBreakOnFailure ? 1 : 0);
2019-08-23 11:56:54 -07:00
AddMenuItem(testMenu, "Enable Console", "Test Enable Console", null, null, true, mTestEnableConsole ? 1 : 0);
//////////
2024-08-27 05:48:22 +02:00
mWindowMenu = root.AddMenuItem("&Window");
AddMenuItem(mWindowMenu, "&Close Document", "Close Document", new => UpdateMenuItem_HasLastActiveDocument);
AddMenuItem(mWindowMenu, "Close &Panel", "Close Panel", new => UpdateMenuItem_HasActivePanel);
2020-09-11 17:47:38 -07:00
AddMenuItem(mWindowMenu, "&Close All", "Close All Panels");
AddMenuItem(mWindowMenu, "Close All Except Current", "Close All Panels Except");
2020-04-30 10:34:37 -07:00
AddMenuItem(mWindowMenu, "&New View into File", "View New", new => UpdateMenuItem_HasActiveDocument);
AddMenuItem(mWindowMenu, "&Split View", "View Split", new => UpdateMenuItem_HasActiveDocument);
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
subMenu = root.AddMenuItem("&Help");
AddMenuItem(subMenu, "&About", "About");
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
IDETabbedView CreateTabbedView()
{
return new IDETabbedView(null);
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public void SetupNewWindow(WidgetWindow window, bool isMainWindow)
{
window.mOnWindowKeyDown.Add(new => SysKeyDown);
2024-07-19 10:31:33 +02:00
window.mOnWindowKeyUp.Add(new => SysKeyUp);
window.mOnMouseUp.Add(new => MouseUp);
2019-09-29 07:44:23 -07:00
if (isMainWindow)
2024-08-27 05:48:22 +02:00
window.mOnWindowCloseQuery.Add(new => SecondaryAllowClose);
}
2019-08-23 11:56:54 -07:00
DarkTabbedView FindDocumentTabbedView()
{
for (int32 windowIdx = 0; windowIdx < mWindows.Count; windowIdx++)
{
2024-08-27 05:48:22 +02:00
var window = mWindows[windowIdx];
var widgetWindow = window as WidgetWindow;
if (widgetWindow != null)
{
var darkDockingFrame = widgetWindow.mRootWidget as DarkDockingFrame;
if (widgetWindow == mMainWindow)
darkDockingFrame = mDockingFrame;
2019-08-23 11:56:54 -07:00
//DarkTabbedView documentTabbedView = null;
DarkTabbedView documentTabbedView = null;
2024-08-27 05:48:22 +02:00
if (darkDockingFrame != null)
{
darkDockingFrame.WithAllDockedWidgets(scope [&] (dockedWidget) =>
{
2019-08-23 11:56:54 -07:00
bool hadSource = false;
2024-08-27 05:48:22 +02:00
var tabbedView = dockedWidget as DarkTabbedView;
if (tabbedView != null)
{
2019-08-23 11:56:54 -07:00
tabbedView.WithTabs(scope [&] (tab) =>
2024-08-27 05:48:22 +02:00
{
2019-08-23 11:56:54 -07:00
if (documentTabbedView != null)
return;
var content = tab.mContent;
if ((content is SourceViewPanel) ||
(content is DisassemblyPanel))
hadSource = true;
2024-08-27 05:48:22 +02:00
});
2019-08-23 11:56:54 -07:00
}
if (hadSource)
documentTabbedView = tabbedView;
2024-08-27 05:48:22 +02:00
});
}
2019-08-23 11:56:54 -07:00
if (documentTabbedView != null)
return documentTabbedView;
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
}
return null;
}
2024-08-27 05:48:22 +02:00
public DarkTabbedView GetDefaultDocumentTabbedView()
{
if ((mActiveDocumentsTabbedView == null) || (mActiveDocumentsTabbedView.mParent == null))
{
mActiveDocumentsTabbedView = CreateTabbedView();
mActiveDocumentsTabbedView.SetRequestedSize(150, 150);
2019-08-23 11:56:54 -07:00
mActiveDocumentsTabbedView.mIsFillWidget = true;
mActiveDocumentsTabbedView.mAutoClose = false;
if ((mProjectPanel != null) && (mProjectPanel.mWidgetWindow != null))
{
if (var tabbedView = mProjectPanel.mParent as TabbedView)
{
if (var dockingFrame = tabbedView.mParent as DockingFrame)
{
dockingFrame.AddDockedWidget(mActiveDocumentsTabbedView, tabbedView, DockingFrame.WidgetAlign.Right);
return mActiveDocumentsTabbedView;
}
}
}
2024-08-27 05:48:22 +02:00
mDockingFrame.AddDockedWidget(mActiveDocumentsTabbedView, null, DockingFrame.WidgetAlign.Right);
}
return mActiveDocumentsTabbedView;
}
void PopulateDocumentMenu(DarkTabbedView tabbedView, Menu menu)
{
WithTabs(scope (tab) =>
{
var menuItem = menu.AddItem(tab.mLabel);
menuItem.mOnMenuItemSelected.Add(new (selMenuItem) =>
{
TabbedView.TabButton activateTab = tab;
activateTab.Activate();
});
});
}
2019-08-23 11:56:54 -07:00
public void WithDocumentTabbedViewsOf(BFWindow window, delegate void(DarkTabbedView) func)
{
var widgetWindow = window as WidgetWindow;
if (widgetWindow != null)
{
2024-08-27 05:48:22 +02:00
var darkDockingFrame = widgetWindow.mRootWidget as DarkDockingFrame;
if (widgetWindow == mMainWindow)
darkDockingFrame = mDockingFrame;
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if (darkDockingFrame != null)
{
darkDockingFrame.WithAllDockedWidgets(scope (dockedWidget) =>
{
var tabbedView = dockedWidget as DarkTabbedView;
if (tabbedView != null)
func(tabbedView);
});
}
}
}
public void WithDocumentTabbedViews(delegate void(DarkTabbedView) func)
{
for (let window in mWindows)
WithDocumentTabbedViewsOf(window, func);
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public void EnsureDocumentArea()
{
GetDefaultDocumentTabbedView();
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public Widget GetActivePanel()
{
2019-08-23 11:56:54 -07:00
if (mRunningTestScript)
return mLastActivePanel;
2024-08-27 05:48:22 +02:00
for (var window in mWindows)
{
2019-08-23 11:56:54 -07:00
if (!window.mHasFocus)
continue;
2024-08-27 05:48:22 +02:00
var widgetWindow = window as WidgetWindow;
if (widgetWindow != null)
{
var focusWidget = widgetWindow.mFocusWidget;
2019-08-23 11:56:54 -07:00
if ((focusWidget == null) && (var hoverWatch = widgetWindow.mRootWidget as HoverWatch))
{
return hoverWatch.mTextPanel;
}
2024-08-27 05:48:22 +02:00
while ((focusWidget != null) && (focusWidget.mParent != null))
{
if (focusWidget.mParent is TabbedView)
return focusWidget;
focusWidget = focusWidget.mParent;
}
}
}
return null;
}
2019-08-23 11:56:54 -07:00
2019-09-29 07:44:23 -07:00
public SourceEditWidgetContent GetActiveSourceEditWidgetContent()
{
let activeWindow = GetActiveWindow();
if (activeWindow.mFocusWidget != null)
{
if (let editWidget = activeWindow.mFocusWidget as EditWidget)
{
let sewc = editWidget.mEditWidgetContent as SourceEditWidgetContent;
if (sewc != null)
2021-12-21 07:13:47 -05:00
{
if (sewc.mEditWidget.mHasFocus)
return sewc;
return null;
}
2019-09-29 07:44:23 -07:00
}
}
var activeTextPanel = GetActivePanel() as TextPanel;
if (activeTextPanel != null)
{
2021-12-21 07:13:47 -05:00
let sewc = activeTextPanel.EditWidget.mEditWidgetContent as SourceEditWidgetContent;
if ((sewc != null) && (sewc.mEditWidget.mHasFocus))
return sewc;
2019-09-29 07:44:23 -07:00
}
return null;
}
2020-09-04 08:58:29 -07:00
public WidgetWindow GetActiveWindow(bool allowModal = false)
2019-08-23 11:56:54 -07:00
{
for (let window in mWindows)
if (window.mHasFocus)
{
var result = window;
2020-09-04 08:58:29 -07:00
while ((result.mWindowFlags.HasFlag(.Modal)) && (result.mParent != null) && (!allowModal))
result = result.mParent;
return result as WidgetWindow;
}
2019-08-23 11:56:54 -07:00
return mMainWindow;
}
public Widget GetActiveDocumentPanel()
{
2024-08-27 05:48:22 +02:00
var activePanel = GetActivePanel();
2019-08-23 11:56:54 -07:00
if ((activePanel is SourceViewPanel) || (activePanel is DisassemblyPanel))
return activePanel;
2024-08-27 05:48:22 +02:00
return null;
2019-08-23 11:56:54 -07:00
}
public Widget GetLastActiveDocumentPanel()
{
2024-08-27 05:48:22 +02:00
var activePanel = GetActiveDocumentPanel();
if (activePanel != null)
return activePanel;
if (mActiveDocumentsTabbedView != null)
{
let activeTab = mActiveDocumentsTabbedView.GetActiveTab();
if (activeTab != null)
{
var lastActivePanel = activeTab.mContent;
if ((lastActivePanel is SourceViewPanel) || (lastActivePanel is DisassemblyPanel))
return lastActivePanel;
2024-08-27 05:48:22 +02:00
}
}
2024-08-27 05:48:22 +02:00
return null;
}
2024-08-27 05:48:22 +02:00
public void WithTabsOf(BFWindow window, delegate void(TabbedView.TabButton) func)
{
WithDocumentTabbedViewsOf(window, scope (documentTabbedView) =>
{
2024-08-27 05:48:22 +02:00
documentTabbedView.WithTabs(func);
});
}
public void WithTabs(delegate void(TabbedView.TabButton) func)
{
for (let window in mWindows)
WithTabsOf(window, func);
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public TabbedView.TabButton GetTab(Widget content)
{
TabbedView.TabButton tab = null;
WithTabs(scope [?] (checkTab) =>
{
if (checkTab.mContent == content)
tab = checkTab;
});
return tab;
}
2019-08-23 11:56:54 -07:00
public void WithSourceViewPanelsOf(BFWindow window, delegate void(SourceViewPanel) func)
{
WithTabsOf(window, scope (tab) =>
{
2024-08-27 05:48:22 +02:00
var sourceViewPanel = tab.mContent as SourceViewPanel;
if (sourceViewPanel != null)
func(sourceViewPanel);
});
}
public void WithSourceViewPanels(delegate void(SourceViewPanel) func)
{
for (let window in mWindows)
WithSourceViewPanelsOf(window, func);
}
2019-08-23 11:56:54 -07:00
TabbedView.TabButton SetupTab(TabbedView tabView, String name, float width, Widget content, bool ownsContent) // 2
{
2021-12-01 00:21:12 +01:00
TabbedView.TabButton tabButton = tabView.AddTab(name, width, content, ownsContent, GetTabInsertIndex(tabView));
2019-08-23 11:56:54 -07:00
if ((var panel = content as Panel) && (var darkTabButton = tabButton as DarkTabbedView.DarkTabButton))
{
darkTabButton.mTabWidthOffset = panel.TabWidthOffset;
}
tabButton.mCloseClickedEvent.Add(new () => CloseDocument(content)); // 1
return tabButton;
}
public DisassemblyPanel ShowDisassemblyPanel(bool clearData = false, bool setFocus = false)
2024-08-27 05:48:22 +02:00
{
DisassemblyPanel disassemblyPanel = null;
TabbedView.TabButton disassemblyTab = null;
2024-08-27 05:48:22 +02:00
WithTabs(scope [&] (tab) =>
{
if ((disassemblyPanel == null) && (tab.mContent is DisassemblyPanel))
{
disassemblyTab = tab;
2024-08-27 05:48:22 +02:00
disassemblyPanel = (DisassemblyPanel)tab.mContent;
}
});
if (disassemblyTab != null)
{
var window = disassemblyTab.mWidgetWindow;
if ((setFocus) && (window != null) && (!HasModalDialogs()) && (!mRunningTestScript))
window.SetForeground();
}
2024-08-27 05:48:22 +02:00
if (disassemblyPanel != null)
2024-12-29 09:03:05 -08:00
{
disassemblyPanel.ClearQueuedData();
disassemblyTab.Activate();
2024-08-27 05:48:22 +02:00
return disassemblyPanel;
2024-12-29 09:03:05 -08:00
}
2024-08-27 05:48:22 +02:00
TabbedView tabbedView = GetDefaultDocumentTabbedView();
disassemblyPanel = new DisassemblyPanel();
2019-08-23 11:56:54 -07:00
//diassemblyPanel.Show(filePath);
2024-08-27 05:48:22 +02:00
var newTabButton = new SourceViewTabButton();
newTabButton.Label = DisassemblyPanel.sPanelName;
newTabButton.mWantWidth = newTabButton.GetWantWidth();
newTabButton.mHeight = tabbedView.mTabHeight;
newTabButton.mContent = disassemblyPanel;
tabbedView.AddTab(newTabButton, GetTabInsertIndex(tabbedView));
newTabButton.mCloseClickedEvent.Add(new () => CloseDocument(disassemblyPanel));
newTabButton.Activate();
2019-08-23 11:56:54 -07:00
//diassemblyPanel.FocusEdit();
mLastActivePanel = disassemblyPanel;
2024-08-27 05:48:22 +02:00
return disassemblyPanel;
}
2019-08-23 11:56:54 -07:00
int GetTabInsertIndex(TabbedView tabs)
{
if (mSettings.mUISettings.mInsertNewTabs == .RightOfExistingTabs)
2022-11-22 01:24:49 +05:00
{
return tabs.mTabs.Count;
2022-11-22 01:24:49 +05:00
}
// Find right-most non-pinned tab
// after which we will put our new tab
int index = 0;
for (index = 0; index < tabs.mTabs.Count; index++)
{
if (tabs.mTabs[index].mIsPinned == false)
{
break;
}
}
return index;
}
2024-08-27 05:48:22 +02:00
public class SourceViewTabButton : DarkTabbedView.DarkTabButton
{
public bool mIsTemp;
2024-08-27 05:48:22 +02:00
public float GetWantWidth()
{
return DarkTheme.sDarkTheme.mSmallFont.GetWidth(mLabel) + DarkTheme.GetScaled(40);
}
2019-08-23 11:56:54 -07:00
2020-02-21 06:18:19 -08:00
public override void Activate(bool setFocus = true)
2019-08-23 11:56:54 -07:00
{
base.Activate(setFocus);
if ((mUpdateCnt > 0) && (mTabbedView.mUpdateCnt == 0) && (mTabbedView.mTabs.Count == 1))
{
//bool isDocument = mContent;
// We were dropped onto a new tabbed view, mark it as a document frame
bool isFillWidget = false;
if (mContent is SourceViewPanel)
isFillWidget = true;
if (mContent is DisassemblyPanel)
isFillWidget = true;
if (isFillWidget)
{
mTabbedView.mIsFillWidget = true;
mTabbedView.mHasFillWidget = true;
}
}
}
2024-08-27 05:48:22 +02:00
public override void Draw(Graphics g)
{
base.Draw(g);
2019-08-23 11:56:54 -07:00
if (mWidth < mWantWidth / 2)
return;
2024-08-27 05:48:22 +02:00
var sourceViewPanel = mContent as SourceViewPanel;
if (sourceViewPanel != null)
2019-08-23 11:56:54 -07:00
{
2024-08-27 05:48:22 +02:00
if (sourceViewPanel.HasUnsavedChanges())
2019-08-23 11:56:54 -07:00
{
g.SetFont(IDEApp.sApp.mTinyCodeFont);
2024-08-27 05:48:22 +02:00
g.DrawString("*", mWantWidth - DarkTheme.sUnitSize + GS!(2), 0);
2019-08-23 11:56:54 -07:00
}
else if (sourceViewPanel.mLoadFailed)
{
g.SetFont(IDEApp.sApp.mCodeFont);
using (g.PushColor(0xFFFF8080))
g.DrawString("!", mWantWidth - DarkTheme.sUnitSize, 0);
}
}
else if (let findResultsPanel = mContent as FindResultsPanel)
{
if (findResultsPanel.IsSearching)
{
g.SetFont(IDEApp.sApp.mTinyCodeFont);
String rotChars = @"/-\|";
StringView sv = .(rotChars, (mUpdateCnt / 16) % 4, 1);
g.DrawString(sv, mWantWidth - DarkTheme.sUnitSize, 0);
}
}
else if (let profilePanel = mContent as ProfilePanel)
{
if (profilePanel.IsSamplingHidden)
{
//using (g.PushColor(((mUpdateCnt / 20) % 2 == 0) ? 0xFFF0F0F0 : 0xFFFFFFFF))
using (g.PushColor(0x80FFFFFF))
g.Draw(DarkTheme.sDarkTheme.GetImage(.RedDot), GS!(8), GS!(0));
}
}
if (mIsTemp)
{
using (g.PushColor(0x80404070))
g.FillRect(0, 0, mWidth, mHeight);
}
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public override void MouseDown(float x, float y, int32 btn, int32 btnCount)
{
2022-11-25 14:29:47 +05:00
if ((mIsRightTab) && (btn == 0) && (btnCount > 1))
{
IDEApp.sApp.MakeTabPermanent(this);
return;
}
2024-08-27 05:48:22 +02:00
base.MouseDown(x, y, btn, btnCount);
2019-08-23 11:56:54 -07:00
if (btn == 1)
{
Menu menu = new Menu();
if (var sourceViewPanel = mContent as SourceViewPanel)
{
2022-11-22 07:37:02 -08:00
var item = menu.AddItem("Copy Full Path");
2019-08-23 11:56:54 -07:00
item.mOnMenuItemSelected.Add(new (menu) =>
{
gApp.SetClipboardText(sourceViewPanel.mFilePath);
});
item = menu.AddItem("Open Containing Folder");
item.mOnMenuItemSelected.Add(new (menu) =>
{
let directory = scope String();
Path.GetDirectoryPath(sourceViewPanel.mFilePath, directory);
ProcessStartInfo procInfo = scope ProcessStartInfo();
procInfo.UseShellExecute = true;
procInfo.SetFileName(directory);
2024-07-16 12:49:15 +02:00
let process = scope SpawnedProcess();
process.Start(procInfo).IgnoreError();
});
item = menu.AddItem("Open in Terminal");
item.mOnMenuItemSelected.Add(new (menu) =>
{
let directory = scope String();
Path.GetDirectoryPath(sourceViewPanel.mFilePath, directory);
ProcessStartInfo procInfo = scope ProcessStartInfo();
procInfo.UseShellExecute = true;
2024-07-16 15:04:15 +02:00
procInfo.SetFileName(gApp.mSettings.mWindowsTerminal);
2024-07-16 12:49:15 +02:00
procInfo.SetWorkingDirectory(directory);
2019-08-23 11:56:54 -07:00
let process = scope SpawnedProcess();
process.Start(procInfo).IgnoreError();
});
2022-08-04 09:02:30 -07:00
item = menu.AddItem("Show in Workspace Panel");
item.mOnMenuItemSelected.Add(new (menu) =>
{
sourceViewPanel.SyncWithWorkspacePanel();
});
2020-09-11 23:26:59 -07:00
item = menu.AddItem("Close");
item.mOnMenuItemSelected.Add(new (menu) =>
{
mCloseClickedEvent();
});
item = menu.AddItem("Close All Except This");
item.mOnMenuItemSelected.Add(new (menu) =>
{
2022-11-22 01:24:49 +05:00
mTabbedView.CloseTabs(false, false, true);
});
item = menu.AddItem("Close All Except Pinned");
item.mOnMenuItemSelected.Add(new (menu) =>
{
mTabbedView.CloseTabs(false, true, false);
2020-09-11 23:26:59 -07:00
});
2022-11-22 07:37:02 -08:00
item = menu.AddItem(this.mIsPinned ? "Unpin Tab" : "Pin Tab");
item.mOnMenuItemSelected.Add(new (menu) =>
{
if (mIsRightTab)
IDEApp.sApp.MakeTabPermanent(this);
mTabbedView.TogglePinned(this);
});
2019-08-23 11:56:54 -07:00
}
if (menu.mItems.Count > 0)
{
SelfToRootTranslate(x, mHeight, var windowX, var windowY);
MenuWidget menuWidget = DarkTheme.sDarkTheme.CreateMenuWidget(menu);
menuWidget.Init(mWidgetWindow.mRootWidget, windowX, windowY);
}
else
delete menu;
}
2024-08-27 05:48:22 +02:00
}
public override void Update()
{
base.Update();
Point point;
if (DarkTooltipManager.CheckMouseover(this, 25, out point))
{
var sourceViewPanel = mContent as SourceViewPanel;
if ((sourceViewPanel != null) && (sourceViewPanel.mFilePath != null))
DarkTooltipManager.ShowTooltip(sourceViewPanel.mFilePath, this, point.x, 14);
}
}
}
public SourceViewPanel FindSourceViewPanel(String filePath)
{
2019-08-23 11:56:54 -07:00
if (filePath == null)
return null;
2024-08-27 05:48:22 +02:00
String useFilePath = scope String(filePath);
if (!IDEUtils.FixFilePath(useFilePath))
2019-08-23 11:56:54 -07:00
return null;
2024-08-27 05:48:22 +02:00
SourceViewPanel sourceViewPanel = null;
WithTabs(scope [&] (tabButton) =>
{
if ((sourceViewPanel == null) && (tabButton.mContent is SourceViewPanel))
{
var checkedResourceViewPanel = (SourceViewPanel)tabButton.mContent;
if (Path.Equals(checkedResourceViewPanel.mFilePath, useFilePath))
sourceViewPanel = checkedResourceViewPanel;
}
});
return sourceViewPanel;
}
void MakeTabPermanent(DarkTabbedView.DarkTabButton tabButton)
{
tabButton.mDragHelper.mAllowDrag = false;
tabButton.mTextColor = Color.White;
tabButton.mIsRightTab = false;
var darkTabbedView = (DarkTabbedView)tabButton.mTabbedView;
darkTabbedView.SetRightTab(null, false);
darkTabbedView.AddTab(tabButton, GetTabInsertIndex(darkTabbedView));
tabButton.Activate();
}
public SourceEditWidget CreateSourceEditWidget(SourceEditWidget refEditWidget = null)
{
var editWidget = new SourceEditWidget(null, refEditWidget);
editWidget.Content.mIsMultiline = true;
editWidget.Content.mWordWrap = false;
editWidget.InitScrollbars(true, true);
var editWidgetContent = (SourceEditWidgetContent)editWidget.Content;
2019-08-23 11:56:54 -07:00
//mEditWidget.mVertScrollbar.mScrollIncrement = editWidgetContent.mFont.GetLineSpacing();
2025-01-29 11:29:39 -08:00
editWidgetContent.mHiliteColor = mSettings.mUISettings.mColors.mCodeHilite;
editWidgetContent.mUnfocusedHiliteColor = mSettings.mUISettings.mColors.mCodeHiliteUnfocused;
editWidgetContent.mHiliteCurrentLine = mSettings.mEditorSettings.mHiliteCurrentLine;
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
return editWidget;
}
2019-08-23 11:56:54 -07:00
2019-11-22 12:27:13 -08:00
public bool CreateEditDataEditWidget(FileEditData editData)
2019-08-23 11:56:54 -07:00
{
if (editData.mEditWidget != null)
return true;
var text = scope String();
if (LoadTextFile(editData.mFilePath, text, true, scope () =>
{
for (int i < text.Length)
{
char8 c = text[i];
if (c == '\r')
{
2019-10-05 10:24:58 -07:00
char8 nextC = 0;
if (i < text.Length - 1)
{
nextC = text[++i];
if (nextC == 0)
{
if (i < text.Length - 2)
nextC = text[++i];
}
2024-08-27 05:48:22 +02:00
}
2019-10-05 10:24:58 -07:00
if (nextC == '\n')
2019-08-23 11:56:54 -07:00
editData.mLineEndingKind = .CrLf;
else
editData.mLineEndingKind = .Cr;
break;
}
else if (c == '\n')
{
editData.mLineEndingKind = .Lf;
break;
}
}
2019-11-22 12:27:13 -08:00
editData.BuildHash(text);
2024-08-27 05:48:22 +02:00
}) case .Err)
2019-08-23 11:56:54 -07:00
return false;
2020-11-04 09:50:41 -08:00
editData..GetFileTime();
2019-08-23 11:56:54 -07:00
mFileWatcher.FileIsValid(editData.mFilePath);
using (mMonitor.Enter())
{
if (editData.mEditWidget != null)
return true;
editData.mEditWidget = CreateSourceEditWidget();
editData.mEditWidget.Content.AppendText(text);
editData.mOwnsEditWidget = true;
editData.mLastFileTextVersion = editData.mEditWidget.Content.mData.mCurTextVersionId;
editData.mEditWidgetCreatedEvent();
}
mFileDataDataRevision++;
return true;
}
2019-11-22 12:27:13 -08:00
public FileEditData GetEditData(String filePath, bool createEditData = true, bool createEditDataWidget = true)
2019-08-23 11:56:54 -07:00
{
FileEditData editData;
using (mMonitor.Enter())
{
String fixedFilePath = scope String(filePath);
IDEUtils.FixFilePath(fixedFilePath);
if (!Environment.IsFileSystemCaseSensitive)
fixedFilePath.ToUpper();
if (!mFileEditData.TryGetValue(fixedFilePath, out editData))
{
if (createEditData)
{
editData = new FileEditData();
editData.mFilePath = new String(filePath);
mFileEditData.Add(new String(fixedFilePath), editData);
}
}
}
if (createEditDataWidget)
2019-11-22 12:27:13 -08:00
CreateEditDataEditWidget(editData);
2019-08-23 11:56:54 -07:00
return editData;
}
public void RenameEditData(String oldPath, String newPath)
{
using (mMonitor.Enter())
{
String oldFixedFilePath = scope String(oldPath);
IDEUtils.FixFilePath(oldFixedFilePath);
if (!Environment.IsFileSystemCaseSensitive)
oldFixedFilePath.ToUpper();
String newFixedFilePath = scope String(newPath);
IDEUtils.FixFilePath(newFixedFilePath);
if (!Environment.IsFileSystemCaseSensitive)
newFixedFilePath.ToUpper();
String outKey;
FileEditData editData;
2020-06-19 06:42:52 -07:00
if (mFileEditData.TryGet(oldFixedFilePath, out outKey, out editData))
2019-08-23 11:56:54 -07:00
{
mFileEditData.Remove(oldFixedFilePath);
delete outKey;
editData.mFilePath.Set(newPath);
String* newKeyPtr;
FileEditData* newEditDataPtr;
if (mFileEditData.TryAdd(newFixedFilePath, out newKeyPtr, out newEditDataPtr))
{
*newKeyPtr = new String(newFixedFilePath);
*newEditDataPtr = editData;
}
else
{
let oldEditData = *newEditDataPtr;
// This can happen if we rename a file to the name of a file that used to exist and is bound to
// another source view panel
WithTabs(scope (tab) =>
{
if (var sourceViewPanel = tab.mContent as SourceViewPanel)
{
if (sourceViewPanel.mEditData == oldEditData)
{
tab.mTabbedView.RemoveTab(tab, true);
}
}
});
for (var projectSource in oldEditData.mProjectSources)
projectSource.mEditData = editData;
gApp.ProcessDeferredDeletes();
oldEditData.Deref();
//editData.Deref();
*newEditDataPtr = editData;
}
}
}
}
public void DeleteEditData(FileEditData editData)
{
if (editData.mOwnsEditWidget)
{
delete editData.mEditWidget;
editData.mEditWidget = null;
editData.mOwnsEditWidget = false;
}
//mFileEditData.Remove(editData);
//delete editData;
}
2024-08-27 05:48:22 +02:00
public FileEditData GetEditData(ProjectSource projectSource, bool createEditWidget = true, SourceHash.Kind hashKind = .MD5)
{
2019-08-23 11:56:54 -07:00
using (mMonitor.Enter())
{
2024-08-27 05:48:22 +02:00
if (projectSource.mEditData == null)
{
2019-08-23 11:56:54 -07:00
String filePath = scope String();
projectSource.GetFullImportPath(filePath);
var editData = GetEditData(filePath, true, false);
if (editData != null)
{
editData.mProjectSources.Add(projectSource);
editData.Ref();
projectSource.mEditData = editData;
}
}
/*editData = CreateEditData(filePath);
2024-08-27 05:48:22 +02:00
/*if (projectSource.mSavedContent == null)
{
editData = CreateEditData(filePath);
}
else
{
editData = new FileEditData();
2019-08-23 11:56:54 -07:00
editData.mFilePath = new String(filePath);
2024-08-27 05:48:22 +02:00
editData.mEditWidget = CreateSourceEditWidget();
editData.mEditWidget.Content.AppendText(projectSource.mSavedContent);
editData.mEditWidget.Content.mData.mTextIdData.DuplicateFrom(projectSource.mSavedCharIdData);
2019-08-23 11:56:54 -07:00
editData.mOwnsEditWidget = true;
2024-08-27 05:48:22 +02:00
}*/
2019-08-23 11:56:54 -07:00
if (editData != null)
{
editData.Ref();
mFileEditData.Add(editData);
projectSource.mEditData = editData;
projectSource.mEditData.mLastFileTextVersion = projectSource.mEditData.mEditWidget.Content.mData.mCurTextVersionId;
2025-03-19 11:01:28 -04:00
}
2024-08-27 05:48:22 +02:00
}
return projectSource.mEditData;*/
2019-08-23 11:56:54 -07:00
}
if (createEditWidget)
CreateEditDataEditWidget(projectSource.mEditData);
return projectSource.mEditData;
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public (SourceViewPanel panel, TabbedView.TabButton tabButton) ShowSourceFile(String filePath, ProjectSource projectSource = null, SourceShowType showType = SourceShowType.ShowExisting, bool setFocus = true)
{
DeleteAndNullify!(mDeferredShowSource);
2019-08-23 11:56:54 -07:00
//TODO: PUT BACK!
//return null;
#unwarn
String useFilePath = filePath;
var useProjectSource = projectSource;
if ((useFilePath == null) && (useProjectSource != null))
{
useFilePath = scope:: String();
useProjectSource.GetFullImportPath(useFilePath);
}
else if (useFilePath != null)
{
useFilePath = scope:: String(useFilePath);
}
int32 emitRevision = -1;
2024-08-27 05:48:22 +02:00
2021-07-31 10:20:21 -07:00
if (useFilePath != null)
{
int barPos = useFilePath.IndexOf('|');
if (barPos != -1)
{
emitRevision = int32.Parse(useFilePath.Substring(barPos + 1)).Value;
useFilePath.RemoveToEnd(barPos);
}
}
2024-08-27 05:48:22 +02:00
if ((useFilePath != null) && (!IDEUtils.FixFilePath(useFilePath)))
return (null, null);
if ((useFilePath == null) & (showType != .New))
return (null, null);
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
SourceViewPanel sourceViewPanel = null;
2019-08-23 11:56:54 -07:00
DarkTabbedView.DarkTabButton sourceViewPanelTab = null;
if ((useProjectSource == null) && (useFilePath != null))
{
useProjectSource = FindProjectSourceItem(useFilePath);
if (useProjectSource != null)
{
var projectSourcePath = scope:: String();
useProjectSource.GetFullImportPath(projectSourcePath);
useFilePath = projectSourcePath;
}
}
void ActivateWindow(WidgetWindow window)
{
if ((setFocus) && (window != null) && (!HasModalDialogs()) && (!mRunningTestScript))
window.SetForeground();
}
2019-08-23 11:56:54 -07:00
if (showType != SourceShowType.New)
{
2020-05-08 07:10:35 -07:00
delegate void(TabbedView.TabButton) tabFunc = scope [&] (tabButton) =>
2024-08-27 05:48:22 +02:00
{
var darkTabButton = (DarkTabbedView.DarkTabButton)tabButton;
if (tabButton.mContent is SourceViewPanel)
{
var checkSourceViewPanel = (SourceViewPanel)tabButton.mContent;
if (checkSourceViewPanel.FileNameMatches(useFilePath))
{
2019-08-23 11:56:54 -07:00
if (sourceViewPanel != null)
{
// Already found one that matches our active tabbed view?
if (sourceViewPanelTab.mTabbedView == mActiveDocumentsTabbedView)
return;
}
sourceViewPanel = checkSourceViewPanel;
sourceViewPanelTab = darkTabButton;
2024-08-27 05:48:22 +02:00
}
}
};
2019-08-23 11:56:54 -07:00
if ((showType == .ShowExistingInActivePanel) && (mActiveDocumentsTabbedView != null))
{
mActiveDocumentsTabbedView.WithTabs(tabFunc);
}
else
WithTabs(tabFunc);
if (sourceViewPanelTab != null)
{
//matchedTabButton = tabButton;
if ((sourceViewPanelTab.mIsRightTab) && (showType != SourceShowType.Temp))
{
2024-08-27 05:48:22 +02:00
MakeTabPermanent(sourceViewPanelTab);
2019-08-23 11:56:54 -07:00
}
if ((useProjectSource != null) &&
2024-08-27 05:48:22 +02:00
(sourceViewPanel.mProjectSource != useProjectSource))
2019-08-23 11:56:54 -07:00
{
//TODO: Change project source in view
sourceViewPanel.AttachToProjectSource(useProjectSource);
2024-08-27 05:48:22 +02:00
//sourceViewPanel.mProjectSource = useProjectSource;
//sourceViewPanel.QueueFullRefresh(true);
2019-08-23 11:56:54 -07:00
}
ActivateWindow(sourceViewPanelTab.mWidgetWindow);
2019-08-23 11:56:54 -07:00
sourceViewPanelTab.Activate(setFocus);
sourceViewPanelTab.mTabbedView.FinishTabAnim();
if (setFocus)
2024-08-27 05:48:22 +02:00
sourceViewPanel.FocusEdit();
sourceViewPanel.CheckEmitRevision();
2019-08-23 11:56:54 -07:00
}
}
2024-08-27 05:48:22 +02:00
if (sourceViewPanel != null)
return (sourceViewPanel, sourceViewPanelTab);
2019-08-23 11:56:54 -07:00
//ShowSourceFile(filePath, projectSource, showTemp, setFocus);
2024-08-27 05:48:22 +02:00
DarkTabbedView tabbedView = GetDefaultDocumentTabbedView();
ActivateWindow(tabbedView.mWidgetWindow);
2024-08-27 05:48:22 +02:00
sourceViewPanel = new SourceViewPanel();
bool success;
2019-08-23 11:56:54 -07:00
if (useProjectSource != null)
2024-08-27 05:48:22 +02:00
{
success = sourceViewPanel.Show(useProjectSource, !mInitialized);
}
else
success = sourceViewPanel.Show(useFilePath, !mInitialized);
sourceViewPanel.mEmitRevision = emitRevision;
if (emitRevision != -1)
sourceViewPanel.mEditWidget.mEditWidgetContent.mIsReadOnly = true;
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if (!success)
{
sourceViewPanel.Close();
2019-08-23 11:56:54 -07:00
delete sourceViewPanel;
2024-08-27 05:48:22 +02:00
return (null, null);
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
var newTabButton = new SourceViewTabButton();
newTabButton.Label = "";
2019-08-23 11:56:54 -07:00
if (useFilePath != null)
2024-08-27 05:48:22 +02:00
Path.GetFileName(useFilePath, newTabButton.mLabel);
2019-08-23 11:56:54 -07:00
else
newTabButton.mLabel.Set("untitled");
2024-08-27 05:48:22 +02:00
newTabButton.mWantWidth = newTabButton.GetWantWidth();
newTabButton.mHeight = tabbedView.mTabHeight;
newTabButton.mContent = sourceViewPanel;
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if (showType == SourceShowType.Temp)
{
let prevAutoClose = tabbedView.mAutoClose;
defer { tabbedView.mAutoClose = prevAutoClose; }
tabbedView.mAutoClose = false;
2024-08-27 05:48:22 +02:00
if (tabbedView.mRightTab != null)
{
CloseDocument(tabbedView.mRightTab.mContent);
Debug.Assert(tabbedView.mRightTab == null);
}
newTabButton.mTextColor = 0xFFC8C8C8;
newTabButton.mIsRightTab = true;
tabbedView.SetRightTab(newTabButton);
}
else
tabbedView.AddTab(newTabButton, GetTabInsertIndex(tabbedView));
newTabButton.mCloseClickedEvent.Add(new () => DocumentCloseClicked(sourceViewPanel));
newTabButton.Activate(setFocus);
if ((setFocus) && (sourceViewPanel.mWidgetWindow != null))
sourceViewPanel.FocusEdit();
return (sourceViewPanel, newTabButton);
}
2019-08-23 11:56:54 -07:00
int GetRecentFilesIdx(String filePath)
2019-08-23 11:56:54 -07:00
{
return mRecentlyDisplayedFiles.FindIndex(scope (item) => Path.Equals(item, filePath));
}
public void UpdateRecentFilesMenuItems(List<String> filesList)
{
}
2024-08-27 05:48:22 +02:00
public void UpdateRecentDisplayedFilesMenuItems()
{
if (mWindowMenu == null)
return;
2019-08-23 11:56:54 -07:00
RecentFiles.UpdateMenu(mRecentlyDisplayedFiles, mWindowMenu, mRecentlyDisplayedFilesMenuItems, true, scope (idx, sysMenu) =>
2019-08-23 11:56:54 -07:00
{
sysMenu.mOnMenuItemSelected.Add(new (evt) => ShowRecentFile(idx));
});
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
public void UpdateRecentFileMenuItems(RecentFiles.RecentKind recentKind)
{
let entry = mSettings.mRecentFiles.mRecents[(int)recentKind];
if (entry.mMenu == null)
return;
RecentFiles.UpdateMenu(entry.mList, entry.mMenu, entry.mMenuItems, false, scope (idx, sysMenu) =>
2019-08-23 11:56:54 -07:00
{
sysMenu.mOnMenuItemSelected.Add(new (evt) => ShowRecentFile(recentKind, idx));
});
entry.mMenu.SetDisabled(entry.mMenuItems.Count == 0);
2019-08-23 11:56:54 -07:00
}
public void UpdateRecentFileMenuItems()
{
if (mWindowMenu == null)
return;
for (RecentFiles.RecentKind recentKind = default; recentKind < RecentFiles.RecentKind.COUNT; recentKind++)
{
UpdateRecentFileMenuItems(recentKind);
}
}
public void AddRecentFile(RecentFiles.RecentKind recentKind, StringView file)
{
mSettings.mRecentFiles.Add(recentKind, file);
UpdateRecentFileMenuItems(recentKind);
}
public void AddToRecentDisplayedFilesList(String path)
{
2020-09-03 06:49:19 -07:00
RecentFiles.Add(mRecentlyDisplayedFiles, path, 20);
2019-08-23 11:56:54 -07:00
UpdateRecentDisplayedFilesMenuItems();
}
2020-09-03 06:49:19 -07:00
public void ShowRecentFileNext()
{
if (mRecentFileSelector == null)
{
mRecentFileSelector = new RecentFileSelector();
mRecentFileSelector.Show();
}
else
mRecentFileSelector.Next();
}
public void ShowRecentFilePrev()
{
if (mRecentFileSelector == null)
{
mRecentFileSelector = new RecentFileSelector();
mRecentFileSelector.Show();
}
else
mRecentFileSelector.Prev();
}
2024-08-27 05:48:22 +02:00
void ShowRecentFile(int idx, bool setFocus = true, bool checkIfExists = false)
{
2020-05-05 07:32:32 -07:00
if (idx >= mRecentlyDisplayedFiles.Count)
return;
2024-08-27 05:48:22 +02:00
String sourceFile = mRecentlyDisplayedFiles[idx];
if (sourceFile == DisassemblyPanel.sPanelName)
{
ShowDisassemblyPanel();
return;
}
if ((checkIfExists) && (!File.Exists(sourceFile)))
return;
2024-08-27 05:48:22 +02:00
ShowSourceFile(sourceFile, null, SourceShowType.ShowExisting, setFocus);
}
2019-08-23 11:56:54 -07:00
void ShowRecentFile(RecentFiles.RecentKind recentKind, int idx, bool setFocus = true)
{
2024-08-27 05:48:22 +02:00
String filePath = mSettings.mRecentFiles.mRecents[(int)recentKind].mList[idx];
2019-08-23 11:56:54 -07:00
switch (recentKind)
{
case .OpenedFile:
ShowSourceFile(filePath, null, SourceShowType.ShowExisting, setFocus);
case .OpenedWorkspace:
var selectedPath = scope String()..AppendF(filePath);
selectedPath.Append(Path.DirectorySeparatorChar);
selectedPath.Append("BeefSpace.toml");
String.NewOrSet!(mDeferredOpenFileName, selectedPath);
mDeferredOpen = .Workspace;
case .OpenedCrashDump:
String.NewOrSet!(mDeferredOpenFileName, filePath);
mDeferredOpen = .CrashDump;
case .OpenedDebugSession:
String.NewOrSet!(mDeferredOpenFileName, filePath);
mDeferredOpen = .DebugSession;
default:
}
}
2024-08-27 05:48:22 +02:00
void DocumentCloseClicked(Widget documentPanel)
{
var sourceViewPanel = documentPanel as SourceViewPanel;
if (sourceViewPanel == null)
{
CloseDocument(documentPanel);
return;
}
2019-08-23 11:56:54 -07:00
// This is a test
// This is a test
2024-08-27 05:48:22 +02:00
if ((!sourceViewPanel.HasUnsavedChanges()) || (!sourceViewPanel.IsLastViewOfData()))
{
CloseDocument(sourceViewPanel);
return;
}
2019-08-23 11:56:54 -07:00
// This is a test
String fileName = scope String();
if (sourceViewPanel.mFilePath != null)
Path.GetFileName(sourceViewPanel.mFilePath, fileName);
else
fileName.Append("untitled");
2024-12-31 14:15:12 -08:00
Dialog aDialog = ThemeFactory.mDefault.CreateDialog("Save file?", scope String()..AppendF("Save changes to '{0}' before closing?", fileName), DarkTheme.sDarkTheme.mIconWarning);
2024-08-27 05:48:22 +02:00
aDialog.mDefaultButton = aDialog.AddButton("Save", new (evt) => { SaveFile(sourceViewPanel); CloseDocument(sourceViewPanel); });
aDialog.AddButton("Don't Save", new (evt) => CloseDocument(sourceViewPanel));
aDialog.mEscButton = aDialog.AddButton("Cancel");
aDialog.PopupWindow(mMainWindow);
}
2019-08-23 11:56:54 -07:00
void RevertSourceViewPanel(SourceViewPanel sourceViewPanel)
{
// When we close a Beef file that has modified text, we need to revert by
// reparsing from the actual source file
2024-08-27 05:48:22 +02:00
if (sourceViewPanel.mIsBeefSource)
{
mBfResolveHelper.DeferReparse(sourceViewPanel.mFilePath, null);
//mBfResolveHelper.DeferRefreshVisibleViews(null);
2024-08-27 05:48:22 +02:00
}
var projectSource = sourceViewPanel.mProjectSource;
2024-08-27 05:48:22 +02:00
if (projectSource != null)
{
var editData = GetEditData(projectSource, true);
if (editData != null)
{
var editWidgetContent = editData.mEditWidget.mEditWidgetContent;
//TODO: Verify this, once we have multiple panes allowed within a single SourceViewContent
if (editWidgetContent.mData.mUsers.Count == 1) // Is last view of data...
{
if ((editData != null) && (editData.mHadRefusedFileChange))
{
// If we didn't take an external file change then closing the file means we want to revert
// our data to the version on disk
sourceViewPanel.Reload();
}
else
{
// Undo until we either get to whatever the last saved state was, or until we
// get to a global action like renaming a symbol - we need to leave those
// so the global undo actually works if invoked from another file
2024-08-27 05:48:22 +02:00
while (editData.HasTextChanged())
{
var nextUndoAction = editWidgetContent.mData.mUndoManager.GetLastUndoAction();
if (nextUndoAction == null)
break;
2024-08-27 05:48:22 +02:00
if (nextUndoAction is UndoBatchEnd)
{
var undoBatchEnd = (UndoBatchEnd)nextUndoAction;
if (undoBatchEnd.Name.StartsWith("#"))
{
break;
}
}
editWidgetContent.mData.mUndoManager.Undo();
}
editWidgetContent.mData.mTextIdData.Prepare();
}
}
2024-08-27 05:48:22 +02:00
}
}
}
2024-08-27 05:48:22 +02:00
public void CloseDocument(Widget documentPanel)
{
bool hasFocus = false;
var sourceViewPanel = documentPanel as SourceViewPanel;
2019-08-23 11:56:54 -07:00
if ((documentPanel.mWidgetWindow != null) && (documentPanel.mWidgetWindow.mFocusWidget != null))
{
if (documentPanel.mWidgetWindow.mFocusWidget.HasParent(documentPanel))
hasFocus = true;
}
2025-03-19 11:01:28 -04:00
/*if (sourceViewPanel != null)
2024-08-27 05:48:22 +02:00
hasFocus = sourceViewPanel.mEditWidget.mHasFocus;*/
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if ((sourceViewPanel != null) && (sourceViewPanel.HasUnsavedChanges()))
RevertSourceViewPanel(sourceViewPanel);
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
DarkTabbedView tabbedView = null;
DarkTabbedView.DarkTabButton tabButton = null;
WithTabs(scope [?] (tab) =>
{
if (tab.mContent == documentPanel)
{
tabbedView = (DarkTabbedView)tab.mTabbedView;
tabButton = (DarkTabbedView.DarkTabButton)tab;
}
});
if (tabbedView == null)
return;
int recentFileIdx = -1;
2024-08-27 05:48:22 +02:00
if (sourceViewPanel != null)
{
if (sourceViewPanel.[Friend]NeedsPostRemoveUpdate)
{
sourceViewPanel.[Friend]mDeleteAfterPostRemoveUpdate = true;
tabButton.mContent.RemoveSelf();
tabButton.mContent = null;
}
else
2024-08-27 05:48:22 +02:00
sourceViewPanel.Dispose();
2019-08-23 11:56:54 -07:00
if (sourceViewPanel.mFilePath != null)
recentFileIdx = GetRecentFilesIdx(sourceViewPanel.mFilePath);
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
/*if (tabButton.mIsRightTab)
tabbedView.SetRightTab(null);
else*/
2024-08-27 05:48:22 +02:00
if (documentPanel is DisassemblyPanel)
2019-08-23 11:56:54 -07:00
recentFileIdx = GetRecentFilesIdx(DisassemblyPanel.sPanelName);
2024-08-27 05:48:22 +02:00
//mRecentFilesList.Remove(DisassemblyPanel.sPanelName);
2019-08-23 11:56:54 -07:00
if (recentFileIdx != -1)
{
delete mRecentlyDisplayedFiles[recentFileIdx];
mRecentlyDisplayedFiles.RemoveAt(recentFileIdx);
2024-08-27 05:48:22 +02:00
UpdateRecentDisplayedFilesMenuItems();
2019-08-23 11:56:54 -07:00
}
2020-05-05 07:32:32 -07:00
if (tabButton.mIsActive)
{
// If this succeeds then tabbUtton.mIsActive will be false, otherwise we do the 'nextTab' logic below
2020-09-25 09:58:30 -07:00
ShowRecentFile(0, hasFocus, true);
2020-05-05 07:32:32 -07:00
}
2019-08-23 11:56:54 -07:00
TabbedView.TabButton nextTab = null;
bool foundRemovedTab = false;
// Select the previous tab or the next one (if this is the first)
2020-04-28 12:55:35 -07:00
if (tabButton.mIsActive)
{
tabbedView.WithTabs(scope [&] (checkTab) =>
2024-08-27 05:48:22 +02:00
{
2020-04-28 12:55:35 -07:00
if (checkTab == tabButton)
foundRemovedTab = true;
else if ((!foundRemovedTab) || (nextTab == null))
nextTab = checkTab;
2024-08-27 05:48:22 +02:00
});
2020-04-28 12:55:35 -07:00
}
2021-02-01 05:34:49 -08:00
else
{
nextTab = tabbedView.GetActiveTab();
}
2019-08-23 11:56:54 -07:00
tabbedView.RemoveTab(tabButton);
if (nextTab != null)
{
nextTab.Activate(hasFocus);
}
else if (tabbedView.mAutoClose)
{
tabbedView.mParentDockingFrame.RemoveDockedWidget(tabbedView);
gApp.DeferDelete(tabbedView);
var documentTabbedView = FindDocumentTabbedView();
if (documentTabbedView != null)
{
Debug.Assert(documentTabbedView != tabbedView);
// If there is some OTHER document window them show that and remove this empty one
documentTabbedView.GetActiveTab().Activate();
}
}
//var intDict = scope Dictionary<String, int>();
2024-08-27 05:48:22 +02:00
/*if (mRecentFilesList.Count >= 1)
{
2019-08-23 11:56:54 -07:00
// Show second-last file
2024-08-27 05:48:22 +02:00
ShowRecentFile(0, hasFocus);
}*/
2019-08-23 11:56:54 -07:00
//var newActiveTab = tabbedView.GetActiveTab();
//if (newActiveTab != null)
//newActiveTab.mContent.SetFocus();
2024-08-27 05:48:22 +02:00
}
void TryCloseCurrentDocument()
{
var activeDocumentPanel = GetLastActiveDocumentPanel();
if (activeDocumentPanel != null)
{
if (activeDocumentPanel is SourceViewPanel)
{
var sourceViewPanel = (SourceViewPanel)activeDocumentPanel;
DocumentCloseClicked(sourceViewPanel);
return;
}
CloseDocument(activeDocumentPanel);
}
}
void TryCloseCurrentPanel()
{
2024-08-27 05:48:22 +02:00
var activeDocumentPanel = GetActiveDocumentPanel();
if (activeDocumentPanel != null)
{
if (activeDocumentPanel is SourceViewPanel)
{
var sourceViewPanel = (SourceViewPanel)activeDocumentPanel;
DocumentCloseClicked(sourceViewPanel);
return;
}
CloseDocument(activeDocumentPanel);
return;
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
var activePanel = GetActivePanel();
if (activePanel != null)
CloseDocument(activePanel);
}
2019-08-23 11:56:54 -07:00
2020-09-11 17:47:38 -07:00
void TryCloseAllDocuments(bool closeCurrent)
2019-08-23 11:56:54 -07:00
{
var docPanels = scope List<Widget>();
2020-09-11 17:47:38 -07:00
Widget skipDocumentPanel = null;
if (!closeCurrent)
skipDocumentPanel = GetActiveDocumentPanel();
2019-08-23 11:56:54 -07:00
WithTabs(scope [&] (tab) =>
2020-09-11 17:47:38 -07:00
{
2024-08-27 05:48:22 +02:00
if ((tab.mContent is SourceViewPanel) || (tab.mTabbedView.mIsFillWidget))
{
if (tab.mContent != skipDocumentPanel)
docPanels.Add(tab.mContent);
}
});
2019-08-23 11:56:54 -07:00
for (var docPanel in docPanels)
DocumentCloseClicked(docPanel);
}
2020-02-07 08:44:06 -08:00
void ViewSplit()
2019-08-23 11:56:54 -07:00
{
var sourceViewPanel = GetActiveSourceViewPanel();
if (sourceViewPanel != null)
{
sourceViewPanel.SplitView();
}
}
[IDECommand]
2024-08-27 05:48:22 +02:00
void CloseCurrentDocument()
{
var activeDocumentPanel = GetActiveDocumentPanel();
if (activeDocumentPanel != null)
CloseDocument(activeDocumentPanel);
}
2024-10-21 09:18:07 -04:00
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);
}
2024-08-27 05:48:22 +02:00
public SourceViewPanel ShowProjectItem(ProjectItem projectItem, bool showTemp = true, bool setFocus = true)
{
if (projectItem is ProjectSource)
{
var projectSource = (ProjectSource)projectItem;
2019-08-23 11:56:54 -07:00
var fullPath = scope String();
projectSource.GetFullImportPath(fullPath);
2024-08-27 05:48:22 +02:00
return ShowSourceFile(fullPath, projectSource, showTemp ? SourceShowType.Temp : SourceShowType.ShowExistingInActivePanel, setFocus).panel;
}
return null;
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public SourceViewPanel ShowSourceFileLocation(String filePath, int showHotIdx, int refHotIdx, int line, int column, LocatorType hilitePosition, bool showTemp = false)
{
var useFilePath = filePath;
if (filePath.StartsWith("$Emit$"))
{
if ((mBfBuildCompiler.IsPerformingBackgroundOperation()) || (mBfResolveCompiler.IsPerformingBackgroundOperation()))
{
DeleteAndNullify!(mDeferredShowSource);
mDeferredShowSource = new DeferredShowSource()
{
mFilePath = new .(filePath),
mShowHotIdx = (.)showHotIdx,
mRefHotIdx = (.)refHotIdx,
mLine = (.)line,
mColumn = (.)column,
mHilitePosition = hilitePosition,
mShowTemp = showTemp
};
return null;
}
String embedFilePath;
bool isViewValid = true;
StringView typeName;
int embedLine;
int embedLineChar;
if (filePath.StartsWith("$Emit$Resolve$"))
{
if (gApp.mSettings.mEditorSettings.mEmitCompiler == .Resolve)
{
var itr = filePath.Split('$');
itr.GetNext();
itr.GetNext();
itr.GetNext();
typeName = itr.GetNext().Value;
2024-08-27 05:48:22 +02:00
mBfResolveCompiler.mBfSystem.Lock(0);
embedFilePath = mBfResolveCompiler.GetEmitLocation(typeName, line, .. scope:: .(), out embedLine, out embedLineChar, var embedHash);
mBfResolveCompiler.mBfSystem.Unlock();
2024-08-27 05:48:22 +02:00
useFilePath = scope:: $"$Emit${useFilePath.Substring("$Emit$Resolve$".Length)}";
}
else
isViewValid = false;
}
else if (filePath.StartsWith("$Emit$Build$"))
{
if (gApp.mSettings.mEditorSettings.mEmitCompiler == .Build)
{
var itr = filePath.Split('$');
itr.GetNext();
itr.GetNext();
itr.GetNext();
typeName = itr.GetNext().Value;
2024-08-27 05:48:22 +02:00
mBfBuildCompiler.mBfSystem.Lock(0);
embedFilePath = mBfBuildCompiler.GetEmitLocation(typeName, line, .. scope:: .(), out embedLine, out embedLineChar, var embedHash);
mBfBuildCompiler.mBfSystem.Unlock();
2024-08-27 05:48:22 +02:00
useFilePath = scope:: $"$Emit${useFilePath.Substring("$Emit$Build$".Length)}";
}
else
isViewValid = false;
}
else
{
var itr = filePath.Split('$');
itr.GetNext();
itr.GetNext();
typeName = itr.GetNext().Value;
mBfBuildCompiler.mBfSystem.Lock(0);
embedFilePath = mBfBuildCompiler.GetEmitLocation(typeName, line, .. scope:: .(), out embedLine, out embedLineChar, var embedHash);
mBfBuildCompiler.mBfSystem.Unlock();
if (gApp.mSettings.mEditorSettings.mEmitCompiler == .Resolve)
{
mBfResolveCompiler.mBfSystem.Lock(0);
mBfResolveCompiler.GetEmitLocation(typeName, line, scope .(), var resolveLine, var resolveLineChar, var resolveHash);
mBfResolveCompiler.mBfSystem.Unlock();
if ((resolveLine != embedLine) || (resolveLineChar != embedLineChar) || (embedHash != resolveHash))
{
isViewValid = false;
useFilePath = scope:: $"$Emit$Build${useFilePath.Substring("$Emit$".Length)}";
}
}
}
if ((isViewValid) && (!embedFilePath.IsEmpty))
{
var sourceViewPanel = ShowSourceFile(scope .(embedFilePath), null, showTemp ? SourceShowType.Temp : SourceShowType.ShowExisting).panel;
if (sourceViewPanel == null)
2024-08-27 05:48:22 +02:00
return null;
var sewc = sourceViewPanel.mEditWidget.mEditWidgetContent as SourceEditWidgetContent;
var data = sewc.mData as SourceEditWidgetContent.Data;
QueuedEmitShowData emitShowData = new .();
emitShowData.mPrevCollapseParseRevision = data.mCollapseParseRevision;
emitShowData.mTypeName = new .(typeName);
emitShowData.mLine = (.)line;
emitShowData.mColumn = (.)column;
DeleteAndNullify!(sourceViewPanel.[Friend]mQueuedEmitShowData);
sourceViewPanel.[Friend]mQueuedEmitShowData = emitShowData;
sourceViewPanel.ShowFileLocation(refHotIdx, embedLine, embedLineChar, .None);
if (typeName.Contains('<'))
{
if (sourceViewPanel.AddExplicitEmitType(typeName))
sourceViewPanel.QueueFullRefresh(false);
}
if (!sourceViewPanel.[Friend]mWantsFullRefresh)
sourceViewPanel.UpdateQueuedEmitShowData();
2025-05-04 07:59:10 +05:00
sourceViewPanel.EditWidget?.Content.RemoveSecondaryTextCursors();
return sourceViewPanel;
}
}
2024-08-27 05:48:22 +02:00
var (sourceViewPanel, tabButton) = ShowSourceFile(useFilePath, null, showTemp ? SourceShowType.Temp : SourceShowType.ShowExisting);
if (sourceViewPanel == null)
return null;
if (((filePath.StartsWith("$")) && (var svTabButton = tabButton as SourceViewTabButton)))
svTabButton.mIsTemp = true;
2024-08-27 05:48:22 +02:00
sourceViewPanel.ShowHotFileIdx(showHotIdx);
sourceViewPanel.ShowFileLocation(refHotIdx, Math.Max(0, line), Math.Max(0, column), hilitePosition);
2025-05-04 07:59:10 +05:00
sourceViewPanel.EditWidget?.Content.RemoveSecondaryTextCursors();
2024-08-27 05:48:22 +02:00
return sourceViewPanel;
}
public SourceViewPanel ShowSourceFileLocation(String filePath, int32 cursorIdx, LocatorType hilitePosition)
{
var sourceViewPanel = ShowSourceFile(filePath).panel;
sourceViewPanel.ShowFileLocation(cursorIdx, hilitePosition);
return sourceViewPanel;
}
public void ShowPCLocation(int32 stackIdx, bool onlyShowCurrent = false, bool wantShowSource = false, bool wantShowDisassembly = false, bool setFocus = true)
{
2019-08-23 11:56:54 -07:00
if (stackIdx == -1)
return;
ClearDeferredUserRequest();
2024-08-27 05:48:22 +02:00
int addr;
String filePath = scope String();
int hotIdx;
int defLineStart;
int defLineEnd;
int line;
int column;
int language;
int stackSize;
mDebugger.CheckCallStack();
String entryName = scope String();
2019-08-23 11:56:54 -07:00
DebugManager.FrameFlags frameFlags;
for (int pass < 2)
{
2024-08-27 05:48:22 +02:00
mDebugger.GetStackFrameInfo(stackIdx, entryName, out addr, filePath, out hotIdx, out defLineStart, out defLineEnd, out line, out column, out language, out stackSize, out frameFlags);
2019-08-23 11:56:54 -07:00
if (!frameFlags.HasFlag(.HasPendingDebugInfo))
break;
2024-08-27 05:48:22 +02:00
2019-08-23 11:56:54 -07:00
int bangPos = entryName.IndexOf('!');
if (bangPos == -1)
break;
2024-08-27 05:48:22 +02:00
2019-08-23 11:56:54 -07:00
String moduleName = scope String(entryName, 0, bangPos);
int result = mDebugger.LoadDebugInfoForModule(moduleName);
if (result == 0)
break;
// If we are backgrounding the load then we'll try to open this up afterward
if (result == 2)
{
SetDeferredUserRequest(new DeferredShowPCLocation(stackIdx));
return;
}
// Try again on result == 1
filePath.Clear();
entryName.Clear();
mCallStackPanel.MarkCallStackDirty();
mThreadPanel.MarkThreadsDirty();
}
bool useWantShowDisassembly = wantShowDisassembly;
2024-08-27 05:48:22 +02:00
for (var breakpoint in mDebugger.mBreakpointList)
{
if (((breakpoint.mAddressRequested) || (breakpoint.mInstrOffset != -1)) &&
(breakpoint.ContainsAddress(addr)))
useWantShowDisassembly = true;
}
2019-08-23 11:56:54 -07:00
String aliasFilePath = null;
String loadCmd = null;
SourceHash hash = default;
int hashPos = filePath.IndexOf('#');
bool checkForOldFileInfo = false;
2019-08-23 11:56:54 -07:00
if (hashPos != -1)
{
let hashStr = StringView(filePath, hashPos + 1);
2019-11-22 12:27:13 -08:00
hash = SourceHash.Create(hashStr);
2019-08-23 11:56:54 -07:00
filePath.RemoveToEnd(hashPos);
if (frameFlags.HasFlag(.CanLoadOldVersion))
{
aliasFilePath = scope:: String(filePath);
String fileText = scope String();
SourceHash fileHash = default;
var hashKind = hash.GetKind();
if (hashKind == .None)
hashKind = .MD5;
LoadTextFile(filePath, fileText, false, scope [&] () => { fileHash = SourceHash.Create(hashKind, fileText); }).IgnoreError();
2019-08-23 11:56:54 -07:00
if (fileHash != hash)
checkForOldFileInfo = true;
}
}
else if (filePath.StartsWith("$Emit"))
{
// Check this later
}
else
{
if (!File.Exists(filePath))
checkForOldFileInfo = true;
}
2019-08-23 11:56:54 -07:00
if (checkForOldFileInfo)
{
String outFileInfo = scope String();
mDebugger.GetStackFrameOldFileInfo(mDebugger.mActiveCallStackIdx, outFileInfo);
var args = outFileInfo.Split!('\n');
if (args.Count == 3)
{
2019-09-23 13:48:11 -07:00
aliasFilePath = scope:: String(filePath);
filePath.Set(args[0]);
loadCmd = scope:: String(args[1]);
2019-08-23 11:56:54 -07:00
}
}
// If we attach to an executable and we happen to have the Diassembly panel shown, STILL just show the source instead of the disassembly at first
if (mIsAttachPendingSourceShow)
{
mInDisassemblyView = false;
mIsAttachPendingSourceShow = false;
}
2024-08-27 05:48:22 +02:00
bool hasSource = filePath.Length != 0;
if ((IsInDisassemblyMode(wantShowSource)) || (!hasSource) || (useWantShowDisassembly))
{
2019-08-23 11:56:54 -07:00
if (frameFlags.HasFlag(.HasPendingDebugInfo))
{
2024-08-27 05:48:22 +02:00
SetDeferredUserRequest(new DeferredShowPCLocation(stackIdx));
2019-08-23 11:56:54 -07:00
}
else
{
var disassemblyPanel = ShowDisassemblyPanel(true, setFocus);
2019-08-23 11:56:54 -07:00
if (aliasFilePath != null)
String.NewOrSet!(disassemblyPanel.mAliasFilePath, aliasFilePath);
2024-08-27 05:48:22 +02:00
disassemblyPanel.Show(addr, filePath, line, column, hotIdx, defLineStart, defLineEnd);
2019-08-23 11:56:54 -07:00
disassemblyPanel.mSourceHash = hash;
}
2024-08-27 05:48:22 +02:00
}
else if (filePath.Length > 0)
{
2019-08-23 11:56:54 -07:00
if ((IsCrashDump) && (!mShowedFirstDocument))
{
mShowedFirstDocument = true;
ShowCallstack();
}
if (filePath.StartsWith("$"))
{
ShowSourceFileLocation(filePath, hotIdx, hotIdx, line, column, .Smart);
return;
}
2024-08-27 05:48:22 +02:00
var (sourceViewPanel, tabButton) = ShowSourceFile(filePath, null, SourceShowType.ShowExisting, setFocus);
if (sourceViewPanel != null)
{
2019-08-23 11:56:54 -07:00
sourceViewPanel.mIsSourceCode = true; // It's always source code, even if there is no extension (ie: stl types like "vector")
if ((aliasFilePath != null) && (var svTabButton = tabButton as SourceViewTabButton))
{
svTabButton.mIsTemp = true;
}
2019-08-23 11:56:54 -07:00
if ((aliasFilePath != null) && (sourceViewPanel.mAliasFilePath == null))
{
2019-08-23 11:56:54 -07:00
String.NewOrSet!(sourceViewPanel.mAliasFilePath, aliasFilePath);
}
2020-03-23 12:07:05 -07:00
2019-08-23 11:56:54 -07:00
if (sourceViewPanel.mLoadFailed)
{
2019-11-22 12:27:13 -08:00
sourceViewPanel.mWantHash = hash;
2019-08-23 11:56:54 -07:00
if (loadCmd != null)
{
sourceViewPanel.SetLoadCmd(loadCmd);
}
}
2020-03-23 12:07:05 -07:00
else if ((gApp.mDebugger.mIsRunningWithHotSwap) && (sourceViewPanel.mIsBeefSource))
{
// No 'wrong hash' warnings
}
else if (((hash != .None) && (sourceViewPanel.mEditData != null) && (!sourceViewPanel.mEditData.CheckHash(hash))) ||
2022-03-08 06:27:06 -08:00
(sourceViewPanel.mHasChangedSinceLastCompile) && (mDebugger?.mIsComptimeDebug != true))
{
sourceViewPanel.ShowWrongHash();
}
2024-08-27 05:48:22 +02:00
int showHotIdx = -1;
if (!onlyShowCurrent)
{
2019-08-23 11:56:54 -07:00
bool checkForChange = false;
if (frameFlags.HasFlag(.WasHotReplaced))
checkForChange = true;
else
{
// If we make a trivial change (ie: characters in a comment) then we won't actually generate a new method
// So don't show as an "old file" if we don't actually have any file changes and we're on the current hot version
if ((sourceViewPanel.mProjectSource != null) && (sourceViewPanel.mProjectSource.mHasChangedSinceLastSuccessfulCompile))
checkForChange = true;
}
if (checkForChange)
2024-08-27 05:48:22 +02:00
{
if (sourceViewPanel.HasTextChangedSinceCompile(defLineStart, defLineEnd, hotIdx))
showHotIdx = hotIdx;
}
2019-08-23 11:56:54 -07:00
/*else
{
if (sourceViewPanel.HasTextChangedSinceCompile(defLineStart, defLineEnd, -1))
showHotIdx = mWorkspace.GetHighestCompileIdx();
}*/
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
sourceViewPanel.ShowHotFileIdx(showHotIdx);
sourceViewPanel.ShowFileLocation(hotIdx, Math.Max(0, line), Math.Max(0, column), LocatorType.Smart);
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
var disassemblyPanel = TryGetDisassemblyPanel(false);
if (disassemblyPanel != null)
disassemblyPanel.Show(addr, filePath, line, column, hotIdx, defLineStart, defLineEnd);
}
}
2019-08-23 11:56:54 -07:00
public override void UnhandledCommandLine(String key, String value)
{
2022-01-07 12:51:57 -03:00
if (File.Exists(key))
{
DragDropFile(key);
return;
}
2024-12-31 14:15:12 -08:00
Fail(scope String()..AppendF("Unhandled command line param: {0}", key));
2019-08-23 11:56:54 -07:00
}
2024-08-27 05:48:22 +02:00
public override bool HandleCommandLineParam(String key, String value)
{
2019-08-23 11:56:54 -07:00
if (mLaunchData != null)
{
if (mLaunchData.mArgs != null)
{
if (!mLaunchData.mArgs.IsEmpty)
mLaunchData.mArgs.Append(" ");
mLaunchData.mArgs.Append(key);
if (value != null)
mLaunchData.mArgs.Append("=", value);
return true;
}
if ((key == "--") && (value == null))
{
mLaunchData.mArgs = new .();
return true;
}
}
2024-08-27 05:48:22 +02:00
if (base.HandleCommandLineParam(key, value))
2019-08-23 11:56:54 -07:00
return true;
if (value == null)
{
switch (key)
{
case "-autoshutdown":
mDebugAutoShutdownCounter = 200;
case "-new":
2024-10-21 09:18:07 -04:00
mVerb = .Open;
2019-08-23 11:56:54 -07:00
case "-testNoExit":
mExitWhenTestScriptDone = false;
2019-10-05 10:24:58 -07:00
case "-firstRun":
2024-08-27 05:48:22 +02:00
mForceFirstRun = true;
2019-10-05 10:24:58 -07:00
mIsFirstRun = true;
2019-08-23 11:56:54 -07:00
case "-clean":
mWantsClean = true;
case "-dbgCompileDump":
mDbgCompileDump = true;
case "-launch":
if (mLaunchData == null)
mLaunchData = new .();
2019-08-23 11:56:54 -07:00
case "-launchPaused":
if (mLaunchData != null)
mLaunchData.mPaused = true;
2024-08-27 05:48:22 +02:00
else
2019-08-23 11:56:54 -07:00
Fail("'-launchPaused' can only be used after '-launch'");
2020-10-15 10:44:29 -07:00
case "-safe":
#if !CLI && BF_PLATFORM_WINDOWS
if (Windows.MessageBoxA(default, "Start the IDE in safe mode? This will disable code intelligence features.", "SAFE MODE?",
Windows.MB_ICONQUESTION | Windows.MB_YESNO) != Windows.IDYES)
{
break;
}
#endif
2021-01-27 15:16:28 -08:00
fallthrough;
2022-01-16 08:00:04 -05:00
case "-forceSafe":
mSafeMode = true;
mNoResolve = true;
#if !CLI
case "-noRecover":
mFileRecovery?.mDisabled = true;
#endif
case "-deterministic":
mDeterministic = true;
2019-08-23 11:56:54 -07:00
default:
return false;
}
return true;
}
else
{
switch (key)
{
#if BF_PLATFORM_WINDOWS
case "-attachHandle":
mProcessAttachHandle = (Windows.EventHandle)int32.Parse(value).GetValueOrDefault();
#endif
case "-attachId":
2024-08-27 05:48:22 +02:00
mProcessAttachId = int32.Parse(value).GetValueOrDefault();
2019-08-23 11:56:54 -07:00
case "-config":
if (mSetConfigName == null)
mSetConfigName = new String();
mSetConfigName.Set(value);
2019-08-23 11:56:54 -07:00
case "-launch":
if (mLaunchData == null)
mLaunchData = new .();
String.NewOrSet!(mLaunchData.mTargetPath, value);
case "-launchDir":
if (mLaunchData != null)
String.NewOrSet!(mLaunchData.mWorkingDir, value);
else
Fail("'-launchDir' can only be used after '-launch'");
2019-08-23 11:56:54 -07:00
#if BF_PLATFORM_WINDOWS
case "-minidump":
String.NewOrSet!(mCrashDumpPath, value);
#endif
case "-platform":
if (mSetPlatformName == null)
mSetPlatformName = new String();
mSetPlatformName.Set(value);
2024-08-27 05:48:22 +02:00
case "-test":
2019-08-23 11:56:54 -07:00
Runtime.SetCrashReportKind(.PrintOnly);
String absTestPath = scope String();
if (mWorkspace.mDir != null)
Path.GetAbsolutePath(value, mWorkspace.mDir, absTestPath);
else
Path.GetFullPath(value, absTestPath);
mWantsClean = true;
mRunningTestScript = true;
2024-08-27 05:48:22 +02:00
mScriptManager.SetTimeoutMS(20 * 60 * 1000); // 20 minute timeout
2019-08-23 11:56:54 -07:00
mScriptManager.QueueCommandFile(absTestPath);
case "-verbosity":
if (value == "quiet")
2024-08-27 05:48:22 +02:00
mVerbosity = .Quiet;
2019-08-23 11:56:54 -07:00
else if (value == "minimal")
2024-08-27 05:48:22 +02:00
mVerbosity = .Minimal;
2019-08-23 11:56:54 -07:00
else if (value == "normal")
2024-08-27 05:48:22 +02:00
mVerbosity = .Normal;
2019-08-23 11:56:54 -07:00
else if (value == "detailed")
2024-08-27 05:48:22 +02:00
mVerbosity = .Detailed;
else if (value == "diagnostic")
2024-08-27 05:48:22 +02:00
mVerbosity = .Diagnostic;
2019-09-04 10:27:37 -07:00
else
Fail(scope String()..AppendF("Invalid verbosity option: {}", value));
2019-08-23 11:56:54 -07:00
case "-workspace","-proddir":
var relDir = scope String(value);
if ((relDir.EndsWith("\\")) || relDir.EndsWith("\""))
2024-08-27 05:48:22 +02:00
relDir.RemoveToEnd(relDir.Length - 1);
2019-08-23 11:56:54 -07:00
IDEUtils.FixFilePath(relDir);
String fullDir = new String();
Path.GetFullPath(relDir, fullDir);
if (fullDir.EndsWith("BeefSpace.toml", .OrdinalIgnoreCase))
fullDir.RemoveFromEnd("BeefSpace.toml".Length);
//TODO: Properly implement 'composite files'
/*if ((File.Exists(fullDir)) ||
((IsBeefFile(fullDir)) && (!Directory.Exists(fullDir))))
2019-08-23 11:56:54 -07:00
{
mWorkspace.mCompositeFile = new CompositeFile(fullDir);
delete fullDir;
}
else*/
2019-08-23 11:56:54 -07:00
mWorkspace.mDir = fullDir;
2019-09-29 07:44:23 -07:00
case "-file":
2022-01-07 12:51:57 -03:00
DragDropFile(value);
2019-08-23 11:56:54 -07:00
default:
return false;
}
return true;
}
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
2022-01-07 12:51:57 -03:00
public void DragDropFile(StringView filePath)
{
let prevDeferredOpen = mDeferredOpen;
if (filePath.EndsWith(".bfdbg", .OrdinalIgnoreCase))
mDeferredOpen = .DebugSession;
else if (filePath.EndsWith(".dmp", .OrdinalIgnoreCase))
mDeferredOpen = .CrashDump;
else if (filePath.EndsWith("BeefSpace.toml", .OrdinalIgnoreCase))
mDeferredOpen = .Workspace;
2022-01-07 12:51:57 -03:00
else
mDeferredOpen = .File;
if (prevDeferredOpen == .File && mDeferredOpen == .File)
{
if (String.IsNullOrEmpty(mDeferredOpenFileName))
String.NewOrSet!(mDeferredOpenFileName, filePath);
else
mDeferredOpenFileName.AppendF("|{}", filePath);
}
else if (prevDeferredOpen != .None && prevDeferredOpen != mDeferredOpen)
2022-01-07 12:51:57 -03:00
{
mDeferredOpen = prevDeferredOpen;
2022-01-07 12:51:57 -03:00
}
else
{
String.NewOrSet!(mDeferredOpenFileName, filePath);
}
}
2024-08-27 05:48:22 +02:00
class Board : Widget
{
public override void Draw(Graphics g)
{
base.Draw(g);
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
using (g.PushColor(0xFFFFFFFF))
g.FillRect(0, 0, 80, 80);
}
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public virtual void Output(String outStr)
{
2019-08-23 11:56:54 -07:00
#if CLI
Console.Write(outStr);
return;
#endif
#unwarn
2024-08-27 05:48:22 +02:00
outStr.Replace("\r", "");
mOutputPanel.Write(outStr);
}
2019-08-23 11:56:54 -07:00
public virtual void OutputSmart(String outStr)
2019-08-23 11:56:54 -07:00
{
#if CLI
Console.Write(outStr);
return;
#endif
#unwarn
if (outStr.Contains('\r'))
2024-08-27 05:48:22 +02:00
{
2019-08-23 11:56:54 -07:00
let newStr = scope String()..Append(outStr);
newStr.Replace("\r", "");
mOutputPanel.WriteSmart(newStr);
}
else
2024-08-27 05:48:22 +02:00
mOutputPanel.WriteSmart(outStr);
2019-08-23 11:56:54 -07:00
}
2024-08-27 05:48:22 +02:00
public virtual void Output(String format, params Object[] args)
{
String outStr = format;
if (args.Count > 0)
2019-08-23 11:56:54 -07:00
{
2024-08-27 05:48:22 +02:00
outStr = scope:: String();
outStr.AppendF(outStr, params args);
2019-08-23 11:56:54 -07:00
}
else
outStr = scope:: String(format);
2024-08-27 05:48:22 +02:00
outStr.Replace("\r", "");
#if CLI
Console.Write(outStr);
#else
2024-08-27 05:48:22 +02:00
mOutputPanel.Write(outStr);
#endif
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
public void CompilerLog(String format, params Object[] args)
{
var str = scope String();
str..AppendF(format, params args);
if (mBfBuildSystem != null)
mBfBuildSystem.Log(str);
if (mBfResolveSystem != null)
mBfResolveSystem.Log(str);
}
2024-08-27 05:48:22 +02:00
public virtual void OutputLine(String format, params Object[] args)
{
String outStr;
if (args.Count > 0)
2019-08-23 11:56:54 -07:00
{
outStr = scope:: String();
2024-08-27 05:48:22 +02:00
outStr.AppendF(format, params args);
2019-08-23 11:56:54 -07:00
}
else
outStr = scope:: String(format);
2024-08-27 05:48:22 +02:00
outStr.Replace("\r", "");
2019-08-23 11:56:54 -07:00
if (mRunningTestScript)
Console.WriteLine(outStr);
#if CLI
Console.WriteLine(outStr);
#else
2024-08-27 05:48:22 +02:00
outStr.Append("\n");
mOutputPanel.Write(outStr);
2019-08-23 11:56:54 -07:00
#endif
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
2024-10-21 09:18:07 -04:00
public virtual void OutputErrorLine(StringView format, params Object[] args)
2019-08-23 11:56:54 -07:00
{
mWantShowOutput = true;
2019-08-23 11:56:54 -07:00
var errStr = scope String();
2024-10-21 09:18:07 -04:00
errStr.Append("ERROR: ");
errStr.Append(format);
2019-08-23 11:56:54 -07:00
OutputLineSmart(errStr, params args);
}
2024-10-21 09:18:07 -04:00
public virtual void OutputWarnLine(StringView format, params Object[] args)
{
var warnStr = scope String();
warnStr.AppendF(format, params args);
#if CLI
var outStr = warnStr;
#else
var outStr = scope String();
outStr.AppendF("{0}{1}{2}", Font.EncodeColor(0xfffef860), warnStr, Font.EncodePopColor());
#endif
OutputLine(outStr);
}
2024-10-21 09:18:07 -04:00
public virtual void OutputLineSmart(StringView format, params Object[] args)
2019-08-23 11:56:54 -07:00
{
2024-08-27 05:48:22 +02:00
String outStr;
if (args.Count > 0)
2019-08-23 11:56:54 -07:00
{
outStr = scope:: String();
2024-08-27 05:48:22 +02:00
outStr.AppendF(format, params args);
2019-08-23 11:56:54 -07:00
}
else
outStr = scope:: String(format);
2024-08-27 05:48:22 +02:00
outStr.Replace("\r", "");
2019-08-23 11:56:54 -07:00
#if CLI
2020-01-12 09:21:50 -08:00
if (outStr.StartsWith("ERROR:"))
mFailed = true;
2019-08-23 11:56:54 -07:00
Console.WriteLine(outStr);
#else
outStr.Append("\n");
if (mOutputPanel != null)
2024-08-27 05:48:22 +02:00
mOutputPanel.WriteSmart(outStr);
2019-08-23 11:56:54 -07:00
#endif
}
2024-08-27 05:48:22 +02:00
public virtual void OutputFormatted(String str, bool isDbgEvalOutput = false)
{
2019-08-23 11:56:54 -07:00
#if CLI
Console.Write(str);
return;
#endif
#unwarn
String useStr = str;
2024-08-27 05:48:22 +02:00
while (true)
{
if (mDebugger.GetRunState() != .NotStarted)
{
int locPos = useStr.IndexOf("^loc:");
if (locPos != -1)
{
String addrSB = scope String();
int i = locPos + 5;
for (; i < useStr.Length; i++)
{
char8 c = useStr[i];
if ((c == 'x') || ((c >= '0') && (c <= '9')) || ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')))
{
addrSB.Append(c);
}
else if (c == '\'')
{
2019-08-23 11:56:54 -07:00
// Allow
2024-08-27 05:48:22 +02:00
}
else
break;
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
int64 memoryAddress = int64.Parse(addrSB, .HexNumber);
String srcLocation = scope String();
mDebugger.GetAddressSourceLocation((int)memoryAddress, srcLocation);
2019-08-23 11:56:54 -07:00
var newStr = scope:: String();
2024-08-27 05:48:22 +02:00
newStr.Append(useStr, 0, locPos);
newStr.Append(srcLocation);
newStr.Append(useStr, i);
2019-08-23 11:56:54 -07:00
useStr = newStr;
2024-08-27 05:48:22 +02:00
continue;
}
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
break;
}
2019-08-23 11:56:54 -07:00
if ((isDbgEvalOutput) && (mIsImmediateDebugExprEval))
mImmediatePanel.WriteResult(useStr);
2024-08-27 05:48:22 +02:00
OutputSmart(useStr);
}
2019-08-23 11:56:54 -07:00
2020-03-29 10:17:28 -07:00
public void PhysSetScale(float scale, bool force = false)
2019-08-23 11:56:54 -07:00
{
var prevScale = DarkTheme.sScale;
float useScale = Math.Clamp(scale, 0.5f, 4.0f);
2019-08-23 11:56:54 -07:00
if ((prevScale == useScale) && (!force))
return;
DarkTheme.SetScale(useScale);
RehupScale();
for (int32 windowIdx = 0; windowIdx < mWindows.Count; windowIdx++)
{
2024-08-27 05:48:22 +02:00
var window = mWindows[windowIdx];
var widgetWindow = window as WidgetWindow;
if (widgetWindow != null)
{
2019-08-23 11:56:54 -07:00
widgetWindow.mRootWidget.RehupScale(prevScale, DarkTheme.sScale);
2024-08-27 05:48:22 +02:00
var darkDockingFrame = widgetWindow.mRootWidget as DarkDockingFrame;
if (widgetWindow == mMainWindow)
darkDockingFrame = mDockingFrame;
if (darkDockingFrame != null)
{
darkDockingFrame.WithAllDockedWidgets(scope (dockedWidget) =>
{
var tabbedView = dockedWidget as DarkTabbedView;
if (tabbedView != null)
{
2019-08-23 11:56:54 -07:00
tabbedView.WithTabs(scope (tab) =>
{
var panel = tab.mContent as Panel;
if ((panel != null) && (panel.mWidgetWindow == null))
{
panel.RehupScale(prevScale, DarkTheme.sScale);
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
});
2024-08-27 05:48:22 +02:00
}
});
}
2019-08-23 11:56:54 -07:00
widgetWindow.mRootWidget.RehupSize();
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
widgetWindow.mIsDirty = true;
}
}
2020-03-29 10:17:28 -07:00
public void SetScale(float scale, bool force = false)
{
PhysSetScale(scale, force);
2020-08-05 05:37:05 -07:00
gApp.mSettings.mUISettings.mScale = DarkTheme.sScale * 100.0f;
2020-03-29 10:17:28 -07:00
}
void MouseUp(MouseEvent evt)
{
if (evt.mBtn == 3)
NavigateBackwards();
else if (evt.mBtn == 4)
NavigateForwards();
}
2024-08-27 05:48:22 +02:00
void SysKeyDown(KeyDownEvent evt)
{
2024-07-23 07:56:23 +02:00
if (!evt.mKeyFlags.HeldKeys.HasFlag(.Alt))
{
2024-08-27 05:48:22 +02:00
#if BF_PLATFORM_WINDOWS
2024-07-23 07:56:23 +02:00
mConsolePanel.SysKeyDown(evt);
mTerminalPanel.SysKeyDown(evt);
2024-08-27 05:48:22 +02:00
#endif
2024-07-23 07:56:23 +02:00
}
2024-07-19 10:31:33 +02:00
2019-11-22 12:27:13 -08:00
if (evt.mHandled)
2019-09-29 07:44:23 -07:00
return;
2024-08-27 05:48:22 +02:00
var window = (WidgetWindow)evt.mSender;
2019-08-23 11:56:54 -07:00
if (window.mFocusWidget is KeysEditWidget)
{
return;
}
2019-09-29 07:44:23 -07:00
IDECommand.ContextFlags useFlags = .None;
var activeWindow = GetActiveWindow();
2020-09-03 06:49:19 -07:00
while (activeWindow.mParent != null)
activeWindow = activeWindow.mParent as WidgetWindow;
if (activeWindow == null)
return;
2019-09-29 07:44:23 -07:00
bool isMainWindow = activeWindow.mRootWidget is MainFrame;
2020-05-20 06:44:21 -07:00
bool isWorkWindow = isMainWindow || (activeWindow.mRootWidget is DarkDockingFrame);
2019-09-29 07:44:23 -07:00
var activePanel = GetActivePanel() as Panel;
if (activePanel is SourceViewPanel)
useFlags |= .Editor;
else if (activePanel is DisassemblyPanel)
useFlags |= .Editor;
if (isMainWindow)
useFlags |= .MainWindow;
2020-05-20 06:44:21 -07:00
if (isWorkWindow)
useFlags |= .WorkWindow;
2019-09-29 07:44:23 -07:00
2019-08-23 11:56:54 -07:00
if (evt.mKeyCode == .Tab)
{
if (activePanel != null)
{
if (activePanel.HandleTab(window.IsKeyDown(.Shift) ? -1 : 1))
return;
}
}
2024-08-27 05:48:22 +02:00
2021-02-25 10:14:22 -08:00
if ((mKeyChordState != null) && (evt.mKeyCode.IsModifier))
{
// Ignore
}
else
{
var keyState = scope KeyState();
keyState.mKeyCode = evt.mKeyCode;
2024-07-19 10:31:33 +02:00
keyState.mKeyFlags = evt.mKeyFlags.HeldKeys;
2019-08-23 11:56:54 -07:00
2021-02-25 10:14:22 -08:00
var curKeyMap = mCommands.mKeyMap;
2019-08-23 11:56:54 -07:00
2021-02-25 10:14:22 -08:00
bool hadChordState = mKeyChordState != null;
if (mKeyChordState != null)
curKeyMap = mKeyChordState.mCommandMap;
var prevKeyChordState = mKeyChordState;
defer delete prevKeyChordState;
mKeyChordState = null;
2019-08-23 11:56:54 -07:00
2021-02-25 10:14:22 -08:00
KeyState matchedKey;
IDECommandBase commandBase;
bool hadMatch = curKeyMap.mMap.TryGet(keyState, out matchedKey, out commandBase);
if ((!hadMatch) && (prevKeyChordState != null))
2019-08-23 11:56:54 -07:00
{
2021-02-25 10:14:22 -08:00
// If we have a "Ctrl+A, Ctrl+B" style sequence then also try to match that against "Ctrl+A, B"
KeyState rawKeyState = keyState;
rawKeyState.mKeyFlags &= ~prevKeyChordState.mKeyState.mKeyFlags;
hadMatch = curKeyMap.mMap.TryGet(rawKeyState, out matchedKey, out commandBase);
2019-08-23 11:56:54 -07:00
}
2021-02-25 10:14:22 -08:00
if (hadMatch)
2019-08-23 11:56:54 -07:00
{
2021-02-25 10:14:22 -08:00
if (var commandMap = commandBase as CommandMap)
{
mKeyChordState = new .();
mKeyChordState.mCommandMap = commandMap;
mKeyChordState.mKeyState = matchedKey;
evt.mHandled = true;
return;
}
else if (var command = commandBase as IDECommand)
2019-08-23 11:56:54 -07:00
{
2021-02-25 10:14:22 -08:00
bool foundMatch = false;
if (useFlags != .None)
2019-08-23 11:56:54 -07:00
{
2021-02-25 10:14:22 -08:00
var checkCommand = command;
while (checkCommand != null)
2019-08-23 11:56:54 -07:00
{
2021-02-25 10:14:22 -08:00
bool matches = checkCommand.mContextFlags == .None;
if (checkCommand.mContextFlags.HasFlag(.Editor))
matches |= useFlags.HasFlag(.Editor);
if (checkCommand.mContextFlags.HasFlag(.MainWindow))
matches |= useFlags.HasFlag(.MainWindow);
if (checkCommand.mContextFlags.HasFlag(.WorkWindow))
matches |= useFlags.HasFlag(.WorkWindow);
2024-08-27 05:48:22 +02:00
2021-02-25 10:14:22 -08:00
if (matches)
{
checkCommand.mAction();
foundMatch = true;
}
checkCommand = checkCommand.mNext;
2019-08-23 11:56:54 -07:00
}
}
2024-08-27 05:48:22 +02:00
2021-02-25 10:14:22 -08:00
if (!foundMatch)
2019-08-23 11:56:54 -07:00
{
2021-02-25 10:14:22 -08:00
var checkCommand = command;
while (checkCommand != null)
2019-08-23 11:56:54 -07:00
{
2021-02-25 10:14:22 -08:00
if (checkCommand.mContextFlags == .None)
{
checkCommand.mAction();
foundMatch = true;
}
checkCommand = checkCommand.mNext;
2019-08-23 11:56:54 -07:00
}
2021-02-25 10:14:22 -08:00
}
2024-08-27 05:48:22 +02:00
2021-02-25 10:14:22 -08:00
if (foundMatch)
{
evt.mHandled = true;
return;
2019-08-23 11:56:54 -07:00
}
}
2021-02-25 10:14:22 -08:00
}
else
{
// Not found
if (hadChordState)
2019-08-23 11:56:54 -07:00
{
2021-02-25 10:14:22 -08:00
Beep(.Error);
2019-08-23 11:56:54 -07:00
evt.mHandled = true;
return;
}
}
}
2024-08-27 05:48:22 +02:00
SourceViewPanel sourceViewPanel = null;
Widget focusWidget = window.mFocusWidget;
if (focusWidget is EditWidget)
focusWidget = focusWidget.mParent;
if (focusWidget is SourceViewPanel)
sourceViewPanel = (SourceViewPanel)focusWidget;
2019-08-23 11:56:54 -07:00
//if (focusWidget is DisassemblyPanel)
2024-08-27 05:48:22 +02:00
//break;
if (evt.mKeyFlags.HeldKeys == 0) // No ctrl/shift/alt
{
switch (evt.mKeyCode)
{
case KeyCode.F2:
if (sourceViewPanel != null)
{
mBookmarkManager.NextBookmark();
}
else if (focusWidget is ProjectPanel)
{
var projectPanel = (ProjectPanel)focusWidget;
projectPanel.TryRenameItem();
}
break;
default:
2019-08-23 11:56:54 -07:00
//OutputLine("Unknown key: {0}", (int)evt.mKeycode);
2024-08-27 05:48:22 +02:00
break;
}
}
if ((evt.mKeyCode == .Return) && (evt.mKeyFlags.HasFlag(.Alt)))
{
// Don't "beep" for any Enter key combinations
window.mFocusWidget?.KeyDown(evt);
evt.mHandled = true;
}
2024-08-27 05:48:22 +02:00
}
2024-07-19 10:31:33 +02:00
void SysKeyUp(KeyCode keyCode)
{
2024-08-27 05:48:22 +02:00
#if BF_PLATFORM_WINDOWS
2024-07-19 10:31:33 +02:00
mConsolePanel.SysKeyUp(keyCode);
2024-07-23 07:56:23 +02:00
mTerminalPanel.SysKeyUp(keyCode);
2024-08-27 05:48:22 +02:00
#endif
}
void ShowOpenFileInSolutionDialog()
{
var widgetWindow = GetCurrentWindow();
if (widgetWindow != null)
{
var openFileInSolutionDialog = new OpenFileInSolutionDialog();
openFileInSolutionDialog.PopupWindow(mMainWindow);
}
2024-07-19 10:31:33 +02:00
}
2019-08-23 11:56:54 -07:00
void ChangeCase(bool toUpper)
{
let sourceViewPanel = GetActiveSourceViewPanel();
if (sourceViewPanel == null)
return;
var ewc = sourceViewPanel.mEditWidget.mEditWidgetContent;
2025-05-04 07:59:10 +05:00
for (var cursor in ewc.mTextCursors)
2019-08-23 11:56:54 -07:00
{
2025-05-04 07:59:10 +05:00
ewc.SetTextCursor(cursor);
if (!ewc.HasSelection())
continue;
2019-08-23 11:56:54 -07:00
2025-05-04 07:59:10 +05:00
/*ewc.mSelection.Value.GetAsForwardSelect(var startPos, var endPos);
for (int i = startPos; i < endPos; i++)
{
var c = ref ewc.mData.mText[i].mChar;
if (toUpper)
c = c.ToUpper;
else
c = c.ToLower;
}*/
2019-08-23 11:56:54 -07:00
2025-05-26 08:25:07 +02:00
var prevSel = ewc.CurSelection.Value;
2019-08-23 11:56:54 -07:00
2025-05-04 07:59:10 +05:00
var str = scope String();
ewc.GetSelectionText(str);
2019-08-23 11:56:54 -07:00
2025-05-04 07:59:10 +05:00
var prevStr = scope String();
prevStr.Append(str);
2019-08-23 11:56:54 -07:00
2025-05-04 07:59:10 +05:00
if (toUpper)
str.ToUpper();
else
str.ToLower();
2019-08-23 11:56:54 -07:00
2025-05-04 07:59:10 +05:00
if (str == prevStr)
continue;
ewc.CreateMultiCursorUndoBatch("IDEApp.ChangeCase()");
ewc.InsertAtCursor(str);
2019-08-23 11:56:54 -07:00
2025-05-26 08:25:07 +02:00
ewc.CurSelection = prevSel;
2025-05-04 07:59:10 +05:00
}
ewc.CloseMultiCursorUndoBatch();
ewc.SetPrimaryTextCursor();
2019-08-23 11:56:54 -07:00
}
public bool IsFilteredOut(String fileName)
{
if (fileName.Contains('~'))
return true;
if (fileName.EndsWith(".TMP", .OrdinalIgnoreCase))
return true;
return false;
}
2024-08-27 05:48:22 +02:00
public static bool IsBeefFile(String fileName)
{
if (fileName.StartsWith('$'))
return true;
2024-08-27 05:48:22 +02:00
return fileName.EndsWith(".cs", StringComparison.OrdinalIgnoreCase) || fileName.EndsWith(".bf", StringComparison.OrdinalIgnoreCase);
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public static bool IsClangSourceFile(String fileName)
{
2019-08-23 11:56:54 -07:00
#if IDE_C_SUPPORT
2024-08-27 05:48:22 +02:00
return fileName.EndsWith(".cpp", StringComparison.OrdinalIgnoreCase) || fileName.EndsWith(".c", StringComparison.OrdinalIgnoreCase) || fileName.EndsWith(".cc", StringComparison.OrdinalIgnoreCase);
2019-08-23 11:56:54 -07:00
#else
return false;
2024-08-27 05:48:22 +02:00
#endif
}
2019-08-23 11:56:54 -07:00
public static bool IsSourceCode(String fileName)
{
if (fileName.StartsWith('$'))
return true;
2019-08-23 11:56:54 -07:00
return fileName.EndsWith(".cs", StringComparison.OrdinalIgnoreCase) || fileName.EndsWith(".bf", StringComparison.OrdinalIgnoreCase) ||
fileName.EndsWith(".h", StringComparison.OrdinalIgnoreCase) || fileName.EndsWith(".cpp", StringComparison.OrdinalIgnoreCase) ||
fileName.EndsWith(".c", StringComparison.OrdinalIgnoreCase) || fileName.EndsWith(".cc", StringComparison.OrdinalIgnoreCase) ||
fileName.EndsWith(".hpp", StringComparison.OrdinalIgnoreCase);
}
2024-08-27 05:48:22 +02:00
public static bool IsClangSourceHeader(String fileName)
{
return fileName.EndsWith(".h", StringComparison.OrdinalIgnoreCase) || fileName.EndsWith(".hpp", StringComparison.OrdinalIgnoreCase);
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public void GetBeefPreprocessorMacros(DefinesSet macroList)
{
2022-01-28 08:19:11 -05:00
var workspaceOptions = GetCurWorkspaceOptions();
2022-04-16 13:23:53 -07:00
var targetTriple = scope String();
if (TargetTriple.IsTargetTriple(gApp.mPlatformName))
targetTriple.Set(gApp.mPlatformName);
else
Workspace.PlatformType.GetTargetTripleByName(gApp.mPlatformName, .GNU, targetTriple);
if (!workspaceOptions.mTargetTriple.IsEmpty)
targetTriple.Set(workspaceOptions.mTargetTriple);
if (targetTriple.StartsWith("x86_64-"))
macroList.Add("BF_MACHINE_X64");
else if (targetTriple.StartsWith("i686-"))
macroList.Add("BF_MACHINE_X86");
else if ((targetTriple.StartsWith("arm64")) || (targetTriple.StartsWith("aarch64")))
macroList.Add("BF_MACHINE_AARCH64");
else if (targetTriple.StartsWith("armv"))
macroList.Add("BF_MACHINE_ARM");
else if (targetTriple.StartsWith("wasm32"))
macroList.Add("BF_MACHINE_WASM32");
else if (targetTriple.StartsWith("wasm64"))
macroList.Add("BF_MACHINE_WASM64");
else
macroList.Add("BF_MACHINE_X64"); // Default
let platform = Workspace.PlatformType.GetFromName(mPlatformName, targetTriple);
2019-10-01 12:46:38 -07:00
if (platform != .Unknown)
{
String def = scope .();
def.Append("BF_PLATFORM_");
platform.ToString(def);
def.ToUpper();
macroList.Add(def);
}
2024-08-27 05:48:22 +02:00
if (workspaceOptions.mRuntimeChecks)
macroList.Add("BF_RUNTIME_CHECKS");
if (workspaceOptions.mEnableObjectDebugFlags)
macroList.Add("BF_ENABLE_OBJECT_DEBUG_FLAGS");
2019-08-23 11:56:54 -07:00
if (workspaceOptions.mAllowHotSwapping)
macroList.Add("BF_ALLOW_HOT_SWAPPING");
bool is64Bits = Workspace.PlatformType.GetPtrSizeByName(gApp.mPlatformName) == 8;
2019-08-23 11:56:54 -07:00
if (is64Bits)
{
if (workspaceOptions.mLargeStrings)
macroList.Add("BF_LARGE_STRINGS");
if (workspaceOptions.mLargeCollections)
macroList.Add("BF_LARGE_COLLECTIONS");
}
if (workspaceOptions.mRuntimeKind == .Disabled)
macroList.Add("BF_RUNTIME_DISABLE");
if ((workspaceOptions.mRuntimeKind == .Reduced) || (workspaceOptions.mRuntimeKind == .Disabled))
macroList.Add("BF_RUNTIME_REDUCED");
if (workspaceOptions.mReflectKind == .Minimal)
macroList.Add("BF_REFLECT_MINIMAL");
2019-08-23 11:56:54 -07:00
// Only supported on Windows at the moment
bool hasLeakCheck = false;
2024-08-27 05:48:22 +02:00
if (workspaceOptions.LeakCheckingEnabled)
2019-08-23 11:56:54 -07:00
{
hasLeakCheck = true;
2024-08-27 05:48:22 +02:00
macroList.Add("BF_ENABLE_REALTIME_LEAK_CHECK");
2019-08-23 11:56:54 -07:00
}
2019-08-23 11:56:54 -07:00
if ((workspaceOptions.mAllocType == .Debug) || (hasLeakCheck))
macroList.Add("BF_DEBUG_ALLOC");
if (workspaceOptions.mEmitDynamicCastCheck)
macroList.Add("BF_DYNAMIC_CAST_CHECK");
if (workspaceOptions.mBuildKind == .Test)
macroList.Add("BF_TEST_BUILD");
macroList.Add("BF_LITTLE_ENDIAN");
if (is64Bits)
macroList.Add("BF_64_BIT");
else
macroList.Add("BF_32_BIT");
//if (workspaceOptions.mE)
2024-08-27 05:48:22 +02:00
macroList.Add("BF_HAS_VDATA_EXTENDER");
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public void GetClangResolveArgs(Project project, List<String> outResolveArgs)
{
using (project.mMonitor.Enter())
{
2019-08-23 11:56:54 -07:00
//List<string> argsList = new List<string>();
2024-08-27 05:48:22 +02:00
var options = GetCurProjectOptions(project);
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if (options == null)
return;
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
outResolveArgs.Add(new String("-std=c++11"));
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if (options.mCOptions.mEnableBeefInterop)
{
2019-10-01 12:46:38 -07:00
var beefPreprocMacros = scope DefinesSet();
2024-08-27 05:48:22 +02:00
GetBeefPreprocessorMacros(beefPreprocMacros);
for (var beefPreprocMacro in beefPreprocMacros.mDefines)
2019-08-23 11:56:54 -07:00
{
2024-08-27 05:48:22 +02:00
outResolveArgs.Add(new String("-D", beefPreprocMacro));
2019-08-23 11:56:54 -07:00
}
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
for (var preprocMacro in options.mCOptions.mPreprocessorMacros)
outResolveArgs.Add(new String("-D", preprocMacro));
for (var includePath in options.mCOptions.mIncludePaths)
2019-08-23 11:56:54 -07:00
{
2024-08-27 05:48:22 +02:00
//outResolveArgs.Add(new String("-I", includePath));
2019-08-23 11:56:54 -07:00
var fullIncludePath = scope String();
2024-08-27 05:48:22 +02:00
project.GetProjectFullPath(includePath, fullIncludePath);
2019-08-23 11:56:54 -07:00
outResolveArgs.Add(new String("-I", fullIncludePath));
}
2024-08-27 05:48:22 +02:00
String llvmDir = scope String(IDEApp.sApp.mInstallDir);
IDEUtils.FixFilePath(llvmDir);
llvmDir.Append("llvm/");
outResolveArgs.Add(new String("-I", llvmDir, "lib/gcc/i686-w64-mingw32/5.2.0/include"));
outResolveArgs.Add(new String("-I", llvmDir, "lib/gcc/i686-w64-mingw32/5.2.0/include-fixed"));
outResolveArgs.Add(new String("-I", llvmDir, "i686-w64-mingw32/include"));
outResolveArgs.Add(new String("-I", llvmDir, "i686-w64-mingw32/include/c++"));
outResolveArgs.Add(new String("-I", llvmDir, "i686-w64-mingw32/include/c++/i686-w64-mingw32"));
outResolveArgs.Add(new String("-I", llvmDir, "i686-w64-mingw32/include/c++/backward"));
outResolveArgs.Add(new String("-I", llvmDir, "bin/../lib/clang/3.7.0/include"));
outResolveArgs.Add(new String("-I", llvmDir, "i686-w64-mingw32/include"));
2019-08-23 11:56:54 -07:00
outResolveArgs.Add(new String("-cc1"));
outResolveArgs.Add(new String("-std=c++11"));
outResolveArgs.Add(new String("-x"));
outResolveArgs.Add(new String("c++"));
/*outResolveArgs.Add(new String("-triple"));
outResolveArgs.Add(new String("x86_64-pc-windows-gnu"));
outResolveArgs.Add(new String("-target-cpu"));
outResolveArgs.Add(new String("x86-64"));
outResolveArgs.Add(new String("-target-feature"));
outResolveArgs.Add(new String("+sse2"));*/
2024-08-27 05:48:22 +02:00
}
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public CompilerBase GetProjectCompilerForFile(String fileName)
{
if (IsBeefFile(fileName))
return mBfResolveCompiler;
2019-08-23 11:56:54 -07:00
#if IDE_C_SUPPORT
2024-08-27 05:48:22 +02:00
if (IsClangSourceFile(fileName))
return mDepClang;
2019-08-23 11:56:54 -07:00
#endif
2024-08-27 05:48:22 +02:00
return null;
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public CompilerBase GetResolveCompilerForFile(String fileName)
{
if (IsBeefFile(fileName))
return mBfResolveCompiler;
2019-08-23 11:56:54 -07:00
#if IDE_C_SUPPORT
2024-08-27 05:48:22 +02:00
if (IsClangSourceFile(fileName))
return mResolveClang;
2019-08-23 11:56:54 -07:00
#endif
2024-08-27 05:48:22 +02:00
return null;
}
public void GetClangFiles(ProjectFolder projectFolder, List<ProjectSource> clangFiles)
{
for (var item in projectFolder.mChildItems)
{
if (item is ProjectSource)
{
var projectSource = (ProjectSource)item;
if (!IsClangSourceFile(projectSource.mPath))
continue;
clangFiles.Add(projectSource);
}
if (item is ProjectFolder)
{
var innerProjectFolder = (ProjectFolder)item;
GetClangFiles(innerProjectFolder, clangFiles);
}
}
}
2019-08-23 11:56:54 -07:00
/*void StopExecutionInstance()
{
if (mExecutionInstance != null)
{
try
{
mExecutionInstance.mProcess.Kill();
}
catch (Exception)
{
}
}
}*/
2022-02-07 14:28:32 -05:00
const int cArgFileThreshold = 0x2000 - 1;
2019-08-23 11:56:54 -07:00
public ExecutionQueueCmd QueueRun(StringView fileName, StringView args, StringView workingDir, ArgsFileKind argsFileKind = .None)
2024-08-27 05:48:22 +02:00
{
var executionQueueCmd = new ExecutionQueueCmd();
executionQueueCmd.mFileName = new String(fileName);
executionQueueCmd.mArgs = new String(args);
executionQueueCmd.mWorkingDir = new String(workingDir);
2022-02-07 14:28:32 -05:00
if (fileName.Length + args.Length + 1 > cArgFileThreshold)
2019-08-23 11:56:54 -07:00
{
// Only use UTF16 if we absolutely need to
if ((argsFileKind == .UTF16WithBom) && (!args.HasMultibyteChars))
2019-08-23 11:56:54 -07:00
executionQueueCmd.mUseArgsFile = .UTF8;
else
2024-08-27 05:48:22 +02:00
executionQueueCmd.mUseArgsFile = argsFileKind;
}
mExecutionQueue.Add(executionQueueCmd);
return executionQueueCmd;
}
void GotExecutionOutputLine(ExecutionInstance executionInstance, String str)
{
if (!String.IsNullOrEmpty(str))
{
using (mMonitor.Enter())
{
executionInstance.mDeferredOutput.Add(new String(str));
}
}
}
2019-08-23 11:56:54 -07:00
static void WriteInputThread(Object obj)
{
ExecutionInstance executionInstance = (ExecutionInstance)obj;
FileStream fileStream = scope FileStream();
if (executionInstance.mProcess.AttachStandardInput(fileStream) case .Err)
return;
2024-08-27 05:48:22 +02:00
WriteLoop:while (!executionInstance.mStdInData.IsEmpty)
{
switch (fileStream.TryWrite(.((.)executionInstance.mStdInData.Ptr, executionInstance.mStdInData.Length)))
{
case .Ok(int len):
executionInstance.mStdInData.Remove(0, len);
case .Err:
break WriteLoop;
}
}
}
2019-08-23 11:56:54 -07:00
static void ReadOutputThread(Object obj)
{
ExecutionInstance executionInstance = (ExecutionInstance)obj;
FileStream fileStream = scope FileStream();
if (executionInstance.mProcess.AttachStandardOutput(fileStream) case .Err)
return;
StreamReader streamReader = scope StreamReader(fileStream, null, false, 4096);
2024-07-23 07:56:23 +02:00
int count = 0;
2019-08-23 11:56:54 -07:00
while (true)
{
2024-07-23 07:56:23 +02:00
count++;
2019-08-23 11:56:54 -07:00
var buffer = scope String();
if (streamReader.ReadLine(buffer) case .Err)
2024-08-27 05:48:22 +02:00
break;
using (IDEApp.sApp.mMonitor.Enter())
executionInstance.mDeferredOutput.Add(new String(buffer));
2019-08-23 11:56:54 -07:00
}
}
static void ReadErrorThread(Object obj)
{
ExecutionInstance executionInstance = (ExecutionInstance)obj;
FileStream fileStream = scope FileStream();
if (executionInstance.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)
2024-08-27 05:48:22 +02:00
break;
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
using (IDEApp.sApp.mMonitor.Enter())
executionInstance.mDeferredOutput.Add(new String(buffer));
2019-08-23 11:56:54 -07:00
}
}
2024-07-23 07:56:23 +02:00
void ReadDebugOutputThread(Object obj)
{
FileStream fileStream = (.)obj;
int count = 0;
2024-08-27 05:48:22 +02:00
Loop:while (true)
2024-07-23 07:56:23 +02:00
{
uint8[4096] data = ?;
switch (fileStream.TryRead(data, -1))
{
case .Ok(let len):
if (len == 0)
break Loop;
using (mDebugOutputMonitor.Enter())
{
for (int i < len)
mDebugOutput.Append((char8)data[i]);
}
case .Err:
break Loop;
}
/*var buffer = scope String();
if (streamReader.Read(buffer) case .Err)
2025-03-19 11:01:28 -04:00
break;
using (mDebugOutputMonitor.Enter())
2024-08-27 05:48:22 +02:00
mDebugOutput.Add(new String(buffer));*/
2024-07-23 07:56:23 +02:00
count++;
}
delete fileStream;
}
/*static void ReadDebugErrorThread(Object obj)
{
ExecutionInstance executionInstance = (ExecutionInstance)obj;
FileStream fileStream = scope FileStream();
if (executionInstance.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)
2025-03-19 11:01:28 -04:00
break;
2024-07-23 07:56:23 +02:00
2025-03-19 11:01:28 -04:00
using (IDEApp.sApp.mMonitor.Enter())
executionInstance.mDeferredOutput.Add(new String(buffer));
2024-07-23 07:56:23 +02:00
}
}*/
public enum RunFlags
{
None,
ShellCommand = 1,
2020-06-22 08:49:23 -07:00
BatchCommand = 2,
NoRedirect = 4,
NoWait = 8,
}
public ExecutionInstance DoRun(StringView inFileName, StringView args, StringView workingDir, ArgsFileKind useArgsFile, Dictionary<String, String> envVars = null, String stdInData = null, RunFlags runFlags = .None, String reference = null)
2024-08-27 05:48:22 +02:00
{
2019-08-23 11:56:54 -07:00
//Debug.Assert(executionInstance == null);
String fileName = scope String(inFileName);
2019-08-23 11:56:54 -07:00
QuoteIfNeeded(fileName);
2024-08-27 05:48:22 +02:00
ProcessStartInfo startInfo = scope ProcessStartInfo();
startInfo.UseShellExecute = false;
2019-08-23 11:56:54 -07:00
if (!fileName.IsEmpty)
2024-08-27 05:48:22 +02:00
startInfo.SetFileName(fileName);
startInfo.SetWorkingDirectory(workingDir);
startInfo.SetArguments(args);
if ((!runFlags.HasFlag(.NoRedirect)) && (!runFlags.HasFlag(.NoWait)))
{
2024-08-27 05:48:22 +02:00
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.RedirectStandardInput = true;
2024-08-27 05:48:22 +02:00
startInfo.CreateNoWindow = true;
}
2024-08-27 05:48:22 +02:00
2020-06-22 08:49:23 -07:00
var executionInstance = new ExecutionInstance();
2020-06-10 07:12:26 -07:00
#if BF_PLATFORM_WINDOWS
2020-06-22 08:49:23 -07:00
if (runFlags.HasFlag(.BatchCommand))
{
String tempFileName = scope String();
Path.GetTempFileName(tempFileName);
tempFileName.Append(".bat");
String shellArgs = scope .();
IDEUtils.AppendWithOptionalQuotes(shellArgs, fileName);
shellArgs.Append(" ");
shellArgs.Append(args);
var result = File.WriteAllText(tempFileName, shellArgs, Encoding.UTF8);
if (result case .Err)
OutputLine("Failed to create temporary batch file");
startInfo.SetFileName(tempFileName);
startInfo.SetArguments("");
executionInstance.mTempFileName = new String(tempFileName);
}
else if (runFlags.HasFlag(.ShellCommand))
{
String shellArgs = scope .();
shellArgs.Append("/s ");
shellArgs.Append("/c ");
shellArgs.Append("\"");
IDEUtils.AppendWithOptionalQuotes(shellArgs, fileName);
if (!args.IsEmpty)
{
shellArgs.Append(" ");
shellArgs.Append(args);
}
shellArgs.Append("\"");
startInfo.SetFileName("cmd.exe");
startInfo.SetArguments(shellArgs);
}
2020-06-10 07:12:26 -07:00
#endif
2019-08-23 11:56:54 -07:00
if (envVars != null)
{
for (var envKV in envVars)
startInfo.AddEnvironmentVariable(envKV.key, envKV.value);
}
2024-08-27 05:48:22 +02:00
if (useArgsFile != .None)
{
String tempFileName = scope String();
Path.GetTempFileName(tempFileName);
2019-08-23 11:56:54 -07:00
Encoding encoding = Encoding.UTF8;
if (useArgsFile == .UTF16WithBom)
encoding = Encoding.UTF16WithBOM;
2024-08-27 05:48:22 +02:00
var result = File.WriteAllText(tempFileName, args, encoding);
2019-08-23 11:56:54 -07:00
if (result case .Err)
OutputLine("Failed to create temporary param file");
2024-08-27 05:48:22 +02:00
String arguments = scope String();
2019-08-23 11:56:54 -07:00
arguments.Concat("@", tempFileName);
startInfo.SetArguments(arguments);
2020-06-22 08:49:23 -07:00
delete executionInstance.mTempFileName;
2024-08-27 05:48:22 +02:00
executionInstance.mTempFileName = new String(tempFileName);
}
2019-08-23 11:56:54 -07:00
if (mVerbosity >= .Detailed)
{
String showArgs = startInfo.[Friend]mArguments;
2019-08-23 11:56:54 -07:00
if ((mRunningTestScript) && (showArgs.Length > 1024))
{
showArgs = scope:: String(showArgs, 0, 1024);
showArgs.Append("...");
}
if (!startInfo.[Friend]mFileName.IsEmpty)
2024-08-27 05:48:22 +02:00
{
if (reference != null)
2024-08-27 05:48:22 +02:00
OutputLine($"Executing ({reference}): {startInfo.[Friend]mFileName} {showArgs}");
else
OutputLine($"Executing: {startInfo.[Friend]mFileName} {showArgs}");
2024-08-27 05:48:22 +02:00
if ((mVerbosity >= .Diagnostic) && (useArgsFile != .None))
OutputLine("Arg file contents: {0}", args);
if ((mVerbosity >= .Diagnostic) && (stdInData != null))
OutputLine("StdIn data: {0}", stdInData);
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
else
OutputLine("Executing: {0}", showArgs);
}
//if (useArgsFile)
2024-08-27 05:48:22 +02:00
{
//if (mVerbosity >= .Detailed)
//OutputLine(" Args: {0}", args);
}
2019-08-23 11:56:54 -07:00
/*var processResult = Process.Start(startInfo);
if (case .Err = processResult)
{
OutputLine("Failed to execute \"{0}\"", fileName);
return executionInstance;
}*/
2024-08-27 05:48:22 +02:00
SpawnedProcess process = new SpawnedProcess();
2019-08-23 11:56:54 -07:00
if (process.Start(startInfo) case .Err)
{
OutputErrorLine("Failed to execute \"{0}\"", inFileName);
2019-08-23 11:56:54 -07:00
delete process;
2020-06-23 07:33:43 -07:00
delete executionInstance;
return null;
2019-08-23 11:56:54 -07:00
}
if (runFlags.HasFlag(.NoWait))
{
delete process;
delete executionInstance;
return null;
}
mExecutionInstances.Add(executionInstance);
2024-08-27 05:48:22 +02:00
executionInstance.mStopwatch.Start();
executionInstance.mProcess = process;
2019-08-23 11:56:54 -07:00
2025-03-21 07:50:15 -04:00
if (startInfo.RedirectStandardOutput)
{
executionInstance.mOutputThread = new Thread(new => ReadOutputThread);
executionInstance.mOutputThread.Start(executionInstance, false);
}
2019-08-23 11:56:54 -07:00
2025-03-21 07:50:15 -04:00
if (startInfo.RedirectStandardError)
{
executionInstance.mErrorThread = new Thread(new => ReadErrorThread);
executionInstance.mErrorThread.Start(executionInstance, false);
}
2019-08-23 11:56:54 -07:00
2025-03-21 07:50:15 -04:00
if ((startInfo.RedirectStandardInput) && (stdInData != null))
{
executionInstance.mStdInData = new String(stdInData);
executionInstance.mInputThread = new Thread(new => WriteInputThread);
executionInstance.mInputThread.Start(executionInstance, false);
}
2024-08-27 05:48:22 +02:00
return executionInstance;
}
2019-08-23 11:56:54 -07:00
protected virtual void BeefCompileStarted()
{
}
protected virtual void BeefCompileDone()
{
}
protected virtual void CompileDone(bool succeeded)
{
}
BuildCompletedCmd GetBuildCompletedCmd()
{
for (int idx = mExecutionQueue.Count - 1; idx >= 0; idx--)
{
if (var buildCompletedCmd = mExecutionQueue[idx] as BuildCompletedCmd)
return buildCompletedCmd;
}
return null;
}
2024-08-27 05:48:22 +02:00
void UpdateExecution()
{
if (mExecutionInstances.Count > 0)
{
var executionInstance = mExecutionInstances[0];
bool failed = false;
2019-08-23 11:56:54 -07:00
bool isDone = false;
using (mMonitor.Enter())
{
2024-08-27 05:48:22 +02:00
for (var str in executionInstance.mDeferredOutput)
2019-08-23 11:56:54 -07:00
{
if (executionInstance.mSmartOutput)
OutputLineSmart(str);
else
OutputLine(str);
2019-08-23 11:56:54 -07:00
delete str;
}
2024-08-27 05:48:22 +02:00
executionInstance.mDeferredOutput.Clear();
2019-08-23 11:56:54 -07:00
}
if (executionInstance.mProcess == null)
{
// Didn't even get off the ground
isDone = true;
failed = true;
executionInstance.mExitCode = -1;
}
2024-08-27 05:48:22 +02:00
else if (executionInstance.mProcess.HasExited)
{
2019-08-23 11:56:54 -07:00
isDone = true;
if (executionInstance.mOutputThread != null)
{
if (!executionInstance.mOutputThread.Join(0))
isDone = false;
}
if (executionInstance.mErrorThread != null)
{
if (!executionInstance.mErrorThread.Join(0))
isDone = false;
}
if (isDone)
{
executionInstance.mExitCode = executionInstance.mProcess.ExitCode;
2024-08-27 05:48:22 +02:00
if (executionInstance.mProcess.ExitCode != 0)
failed = true;
executionInstance.mProcess.WaitFor(-1);
executionInstance.mProcess.Close();
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
executionInstance.mStopwatch.Stop();
if (executionInstance.mIsTargetRun)
{
mTargetExitCode = executionInstance.mExitCode;
2019-08-23 11:56:54 -07:00
}
else
{
2024-08-27 05:48:22 +02:00
if (executionInstance.mParallelGroup == -1)
{
if (mVerbosity >= .Detailed)
2024-08-27 05:48:22 +02:00
OutputLine("Execution time: {0:0.00}s", executionInstance.mStopwatch.ElapsedMilliseconds / 1000.0f);
}
2019-08-23 11:56:54 -07:00
if (executionInstance.mCanceled)
OutputLine("Execution Canceled");
else if (failed)
OutputLine("Execution Failed");
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if (executionInstance.mTempFileName != null)
{
File.Delete(executionInstance.mTempFileName).IgnoreError();
}
2019-08-23 11:56:54 -07:00
}
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if (failed)
{
if (mExecutionQueue.Count > 0)
{
var buildCompletedCmd = GetBuildCompletedCmd();
if (buildCompletedCmd != null)
buildCompletedCmd.mFailed = true;
}
}
2019-08-23 11:56:54 -07:00
if (isDone)
{
mExecutionInstances.RemoveAt(0);
executionInstance.mDone = true;
2019-08-23 11:56:54 -07:00
if (executionInstance.mAutoDelete)
delete executionInstance;
}
/*if (failed)
{
OutputLine("COMPILATION FAILED");
mExecutionQueue.Clear();
}
if ((executionInstance == null) && (mExecutionQueue.Count == 0))
2025-03-19 11:01:28 -04:00
{
2019-08-23 11:56:54 -07:00
OutputLine("Compilation finished.");
}*/
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
bool buildFailed = false;
if ((mBuildContext != null) && (mBuildContext.Failed))
buildFailed = true;
let buildCompleteCmd = GetBuildCompletedCmd();
if ((buildCompleteCmd != null) && (buildCompleteCmd.mFailed))
buildFailed = true;
2024-08-27 05:48:22 +02:00
bool canExecuteNext = false;
int32 parallelExecutionCount = 16;
if ((mExecutionQueue.Count > 0) && (mExecutionInstances.Count < parallelExecutionCount))
{
var executionQueueCmd = mExecutionQueue[0] as ExecutionQueueCmd;
if (executionQueueCmd != null)
{
if (mExecutionInstances.Count > 0)
{
var executionInstance = mExecutionInstances[0];
if ((executionQueueCmd.mParallelGroup != -1) &&
(executionQueueCmd.mParallelGroup == executionInstance.mParallelGroup))
canExecuteNext = true;
}
else
canExecuteNext = true;
}
else if (mExecutionInstances.Count == 0)
canExecuteNext = true;
}
if (canExecuteNext)
{
var next = mExecutionQueue[0];
2019-08-23 11:56:54 -07:00
bool waitForBuildClang = false;
#if IDE_C_SUPPORT
waitForBuildClang = (mDepClang.mCompileWaitsForQueueEmpty) && (mDepClang.HasQueuedCommands());
#endif
2024-08-27 05:48:22 +02:00
if ((next is ProcessBfCompileCmd) && (mBfBuildCompiler.HasQueuedCommands() || (waitForBuildClang)))
return;
/*if (next is BuildCompletedCmd)
{
if (mBuildContext != null)
return;
}*/
if (let targetCompletedCmd = next as TargetCompletedCmd)
{
if ((mBuildContext == null) || (!mBuildContext.Failed))
{
if (!targetCompletedCmd.mIsReady)
return;
}
}
2021-02-25 10:14:22 -08:00
if (let scriptCmd = next as ScriptCmd)
{
if (mBuildContext?.mScriptManager != null)
{
if (scriptCmd.mCmd != null)
{
mBuildContext.mScriptManager.QueueCommands(scriptCmd.mCmd, scriptCmd.mPath, .NoLines);
DeleteAndNullify!(scriptCmd.mCmd);
}
2021-03-02 06:29:53 -08:00
if ((mBuildContext.mScriptManager.HasQueuedCommands) && (!mBuildContext.mScriptManager.mFailed))
2021-02-25 10:14:22 -08:00
return;
}
}
2024-08-27 05:48:22 +02:00
#if BF_PLATFORM_WINDOWS
if (let startDebugCmd = next as StartDebugCmd)
{
if ((mSettings.mDebugConsoleKind == .Native) && (mSettings.mKeepNativeConsoleOpen))
{
if (!startDebugCmd.mConnectedToConsole)
{
if (startDebugCmd.mConsoleProcessId == 0)
{
int32 processId = 0;
List<Process> processList = scope .();
Process.GetProcesses(processList);
defer processList.ClearAndDeleteItems();
for (var process in processList)
{
if ((process.ProcessName.Contains("BeefCon.exe")) || (process.ProcessName.Contains("BeefCon_d.exe")))
{
var title = process.GetMainWindowTitle(.. scope .());
if (title.EndsWith("Debug Console"))
{
processId = process.Id;
}
}
}
if (processId == 0)
{
var beefConExe = scope $"{gApp.mInstallDir}/BeefCon.exe";
ProcessStartInfo procInfo = scope ProcessStartInfo();
procInfo.UseShellExecute = false;
procInfo.SetFileName(beefConExe);
procInfo.SetArguments(scope $"{Process.CurrentId}");
procInfo.ActivateWindow = false;
var process = scope SpawnedProcess();
if (process.Start(procInfo) case .Ok)
{
processId = (.)process.ProcessId;
}
}
startDebugCmd.mConsoleProcessId = processId;
}
if (startDebugCmd.mConsoleProcessId != 0)
{
if (WinNativeConsoleProvider.AttachConsole(startDebugCmd.mConsoleProcessId))
{
// Worked
WinNativeConsoleProvider.ClearConsole();
mConsolePanel.mBeefConAttachState = .Attached(startDebugCmd.mConsoleProcessId);
startDebugCmd.mConnectedToConsole = true;
}
else
{
// Keep trying to attach
return;
}
}
}
}
2024-08-27 05:48:22 +02:00
}
#endif
2024-08-27 05:48:22 +02:00
defer delete next;
mExecutionQueue.RemoveAt(0);
bool ignoreCommand = false;
if (next.mOnlyIfNotFailed)
{
if (mExecutionQueue.Count > 0)
{
var buildCompletedCmd = GetBuildCompletedCmd();
if ((buildCompletedCmd != null) && (buildCompletedCmd.mFailed))
ignoreCommand = true;
}
}
2024-08-27 05:48:22 +02:00
if (ignoreCommand)
{
2019-08-23 11:56:54 -07:00
// Nothing
if (let targetCompletedCmd = next as TargetCompletedCmd)
{
String projectBuildDir = scope String();
gApp.GetProjectBuildDir(targetCompletedCmd.mProject, projectBuildDir);
gApp.mBfBuildCompiler.SetBuildValue(projectBuildDir, "Link", "FAILED");
gApp.mBfBuildCompiler.WriteBuildCache(projectBuildDir);
}
2024-08-27 05:48:22 +02:00
}
else if (next is ProcessBfCompileCmd)
{
var processCompileCmd = (ProcessBfCompileCmd)next;
2019-08-23 11:56:54 -07:00
mCompilingBeef = false;
BeefCompileDone();
//processCompileCmd.mStopwatch.Stop();
if ((processCompileCmd.mHadBeef) && (mVerbosity >= .Normal))
2024-08-27 05:48:22 +02:00
OutputLine("Beef compilation time: {0:0.00}s", processCompileCmd.mStopwatch.ElapsedMilliseconds / 1000.0f);
2019-08-23 11:56:54 -07:00
if (!mWorkspace.mCompileInstanceList.IsEmpty)
2019-08-23 11:56:54 -07:00
{
var compileInstance = mWorkspace.mCompileInstanceList.Back;
2019-08-23 11:56:54 -07:00
compileInstance.mCompileResult = .Failure;
if (processCompileCmd.mBfPassInstance.mCompileSucceeded)
{
//foreach (var sourceViewPanel in GetSourceViewPanels())
WithSourceViewPanels(scope (sourceViewPanel) =>
{
sourceViewPanel.mHasChangedSinceLastCompile = false;
});
compileInstance.mCompileResult = .Success;
}
else
{
compileInstance.mCompileResult = .Failure;
}
2019-08-23 11:56:54 -07:00
ProcessBeefCompileResults(processCompileCmd.mBfPassInstance, processCompileCmd.mCompileKind, processCompileCmd.mHotProject, processCompileCmd.mStopwatch);
processCompileCmd.mBfPassInstance = null;
if (mHotResolveState != .None)
{
if (compileInstance.mCompileResult == .Success)
compileInstance.mCompileResult = .PendingHotLoad;
}
2019-08-23 11:56:54 -07:00
}
if (processCompileCmd.mProfileCmd != null)
{
mExecutionQueue.Add(processCompileCmd.mProfileCmd);
processCompileCmd.mProfileCmd = null;
}
delete processCompileCmd.mStopwatch;
processCompileCmd.mStopwatch = null;
2024-08-27 05:48:22 +02:00
}
else if (next is TargetCompletedCmd)
{
if (!buildFailed)
{
2024-08-27 05:48:22 +02:00
var targetCompletedCmd = (TargetCompletedCmd)next;
targetCompletedCmd.mProject.mNeedsTargetRebuild = false;
targetCompletedCmd.mProject.mForceCustomCommands = false;
}
2024-08-27 05:48:22 +02:00
}
else if (next is StartDebugCmd)
{
if (!buildFailed)
{
var startDebugCmd = (StartDebugCmd)next;
2024-08-27 05:48:22 +02:00
if (StartupProject(true, startDebugCmd.mWasCompiled))
{
OutputLine("Debugger started");
}
else
OutputLine("Failed to start debugger");
/*#if BF_PLATFORM_WINDOWS
if ((mSettings.mDebugConsoleKind == .Native) && (mSettings.mKeepNativeConsoleOpen))
{
BeefConConsoleProvider.Pipe pipe = scope .();
//pipe.Connect(Process.CurrentId, )
pipe.Connect(123, -startDebugCmd.mConsoleProcessId).IgnoreError();
pipe.StartMessage(.Attached);
pipe.Stream.Write((int32)mDebugger.GetProcessId());
pipe.EndMessage();
}
2024-08-27 05:48:22 +02:00
#endif*/
}
2024-08-27 05:48:22 +02:00
}
else if (next is ExecutionQueueCmd)
{
var executionQueueCmd = (ExecutionQueueCmd)next;
ReplaceVariables(executionQueueCmd.mFileName);
ReplaceVariables(executionQueueCmd.mArgs);
ReplaceVariables(executionQueueCmd.mWorkingDir);
if (executionQueueCmd.mEnvVars != null)
{
for (let kv in executionQueueCmd.mEnvVars)
ReplaceVariables(kv.value);
}
2024-08-27 05:48:22 +02:00
var executionInstance = DoRun(executionQueueCmd.mFileName, executionQueueCmd.mArgs, executionQueueCmd.mWorkingDir, executionQueueCmd.mUseArgsFile,
executionQueueCmd.mEnvVars, executionQueueCmd.mStdInData, executionQueueCmd.mRunFlags, executionQueueCmd.mReference);
if (executionInstance != null)
{
2024-08-27 05:48:22 +02:00
executionInstance.mParallelGroup = executionQueueCmd.mParallelGroup;
executionInstance.mIsTargetRun = executionQueueCmd.mIsTargetRun;
}
2024-08-27 05:48:22 +02:00
}
else if (next is BuildCompletedCmd)
{
var buildCompletedCmd = (BuildCompletedCmd)next;
2019-08-23 11:56:54 -07:00
#if IDE_C_SUPPORT
2024-08-27 05:48:22 +02:00
mWorkspace.WithProjectItems(scope (projectItem) =>
{
var projectSource = projectItem as ProjectSource;
if (projectItem != null)
{
2019-08-23 11:56:54 -07:00
String fullPath = scope String();
projectSource.GetFullImportPath(fullPath);
2024-08-27 05:48:22 +02:00
if (completedCompileCmd.mClangCompiledFiles.Contains(fullPath))
{
mDepClang.QueueCheckDependencies(projectSource, ClangCompiler.DepCheckerType.Clang);
}
2025-03-19 11:01:28 -04:00
}
2024-08-27 05:48:22 +02:00
});
if (!completedCompileCmd.mFailed)
mDepClang.mDoDependencyCheck = false;
2019-08-23 11:56:54 -07:00
#endif
if (buildFailed)
buildCompletedCmd.mFailed = true;
2024-08-27 05:48:22 +02:00
2019-08-23 11:56:54 -07:00
CompileResult(buildCompletedCmd.mHotProjectName, !buildCompletedCmd.mFailed);
2024-08-27 05:48:22 +02:00
if (buildCompletedCmd.mFailed)
OutputLineSmart("ERROR: BUILD FAILED. Total build time: {0:0.00}s", buildCompletedCmd.mStopwatch.ElapsedMilliseconds / 1000.0f);
else if ((mVerbosity >= .Detailed) && (buildCompletedCmd.mStopwatch != null))
OutputLineSmart("SUCCESS: Build completed with no errors. Total build time: {0:0.00}s", buildCompletedCmd.mStopwatch.ElapsedMilliseconds / 1000.0f);
2019-08-23 11:56:54 -07:00
2022-03-08 06:27:06 -08:00
if (mDebugger?.mIsComptimeDebug == true)
DebuggerComptimeStop();
2019-08-23 11:56:54 -07:00
CompileDone(!buildCompletedCmd.mFailed);
if (mTestManager != null)
{
if (buildCompletedCmd.mFailed)
mTestManager.BuildFailed();
else
mTestManager.Start();
}
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
else if (var profileCmd = next as ProfileCmd)
{
if (gApp.mDebugger.mIsRunning)
mProfilePanel.StartProfiling(profileCmd.mThreadId, profileCmd.mDesc, profileCmd.mSampleRate);
}
2021-02-25 10:14:22 -08:00
else if (var scriptCmd = next as ScriptCmd)
{
// Already handled
(void)scriptCmd;
}
else if (var writeEmitCmd = next as WriteEmitCmd)
{
String projectName = new String(writeEmitCmd.mProjectName);
String filePath = new String(writeEmitCmd.mPath);
mBfBuildCompiler.DoBackground(new () =>
{
if (var project = mWorkspace.FindProject(projectName))
{
var bfProject = mBfBuildSystem.GetBfProject(project);
if (bfProject != null)
{
mBfBuildSystem.Lock(0);
mBfBuildCompiler.WriteEmitData(filePath, bfProject);
mBfBuildSystem.Unlock();
}
}
}
~
{
delete projectName;
delete filePath;
});
}
2024-10-18 09:13:24 -04:00
else if (var emsdkCmd = next as EmsdkInstalledCmd)
{
gApp.mSettings.mEmscriptenPath.Set(emsdkCmd.mPath);
gApp.mSettings.Save();
}
2024-08-27 05:48:22 +02:00
else
{
Runtime.FatalError("Unknown command");
}
}
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public bool ParseSourceFile(BfSystem bfSystem, BfPassInstance passInstance, ProjectSource projectSource)
{
bool worked = true;
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if (!IsBeefFile(projectSource.mPath))
return true;
2019-08-23 11:56:54 -07:00
var fullPath = scope String();
projectSource.GetFullImportPath(fullPath);
2024-08-27 05:48:22 +02:00
String data = scope String();
LoadTextFile(fullPath, data);
if (data == null)
{
OutputErrorLine("FAILED TO LOAD FILE: {0}", fullPath);
return true;
}
mFileWatcher.FileIsValid(fullPath);
var bfParser = bfSystem.CreateParser(projectSource);
bfParser.SetSource(data, fullPath, -1);
worked &= bfParser.Parse(passInstance, false);
worked &= bfParser.Reduce(passInstance);
worked &= bfParser.BuildDefs(passInstance, null, false);
return worked;
}
public bool FindProjectSourceContent(ProjectSource projectSource, out IdSpan char8IdData, bool loadOnFail, String sourceContent, SourceHash* sourceHash)
{
char8IdData = IdSpan();
var fullPath = scope String();
2019-08-23 11:56:54 -07:00
projectSource.GetFullImportPath(fullPath);
2024-08-27 05:48:22 +02:00
2019-08-23 11:56:54 -07:00
//SourceViewPanel sourceViewPanel = null;
2024-08-27 05:48:22 +02:00
using (mMonitor.Enter())
{
if (projectSource.mEditData != null)
{
2019-08-23 11:56:54 -07:00
if (projectSource.mEditData.IsFileDeleted())
{
return false;
}
if (projectSource.mEditData.mEditWidget != null)
{
var idData = ref projectSource.mEditData.mEditWidget.Content.mData.mTextIdData;
idData.Prepare();
2024-08-27 05:48:22 +02:00
if (!projectSource.mEditData.mSavedCharIdData.Equals(idData))
{
char8IdData = projectSource.mEditData.mEditWidget.mEditWidgetContent.mData.mTextIdData.Duplicate();
projectSource.mEditData.mEditWidget.GetText(sourceContent);
2020-03-23 12:07:05 -07:00
if (sourceHash != null)
{
*sourceHash = SourceHash.Create(.MD5, sourceContent, projectSource.mEditData.mLineEndingKind);
if (*sourceHash case .MD5(let md5Hash))
projectSource.mEditData.mMD5Hash = md5Hash;
}
2019-08-23 11:56:54 -07:00
return true;
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
}
if (projectSource.mEditData.mSavedContent != null)
{
2024-08-27 05:48:22 +02:00
char8IdData = projectSource.mEditData.mSavedCharIdData.Duplicate();
sourceContent.Set(projectSource.mEditData.mSavedContent);
2020-03-23 12:07:05 -07:00
if ((!projectSource.mEditData.mMD5Hash.IsZero) && (sourceHash != null))
*sourceHash = .MD5(projectSource.mEditData.mMD5Hash);
2019-08-23 11:56:54 -07:00
return true;
}
2024-08-27 05:48:22 +02:00
}
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if (loadOnFail)
{
String text = scope String();
2019-08-23 11:56:54 -07:00
bool isValid = false;
2024-08-27 05:48:22 +02:00
if (LoadTextFile(fullPath, text, true, scope [?] () => { if (sourceHash != null) *sourceHash = SourceHash.Create(.MD5, text); }) case .Ok)
2020-03-23 12:07:05 -07:00
{
2024-08-27 05:48:22 +02:00
mFileWatcher.FileIsValid(fullPath);
2019-08-23 11:56:54 -07:00
isValid = true;
}
2024-08-27 05:48:22 +02:00
char8IdData = IdSpan.GetDefault((int32)text.Length);
2019-08-23 11:56:54 -07:00
var editData = GetEditData(projectSource, false);
using (mMonitor.Enter())
2024-08-27 05:48:22 +02:00
{
2019-08-23 11:56:54 -07:00
if (isValid)
{
editData.SetSavedData(text, char8IdData);
sourceContent.Set(text);
}
2024-08-27 05:48:22 +02:00
else
2019-08-23 11:56:54 -07:00
{
editData.SetSavedData(null, IdSpan());
editData.mFileDeleted = true;
}
2020-03-23 12:07:05 -07:00
if (sourceHash != null)
{
if (*sourceHash case .MD5(let md5Hash))
editData.mMD5Hash = md5Hash;
}
2020-11-04 09:50:41 -08:00
editData.GetFileTime();
2019-08-23 11:56:54 -07:00
}
return isValid;
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
return false;
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public void SetInDisassemblyView(bool inDisassemblyView)
{
2019-08-23 11:56:54 -07:00
if (mShuttingDown)
return;
2024-08-27 05:48:22 +02:00
if (mInDisassemblyView != inDisassemblyView)
{
2019-08-23 11:56:54 -07:00
// We need to refresh auto watch panel so we can handle displaying registers when switching to disasm
2024-08-27 05:48:22 +02:00
mAutoWatchPanel.MarkWatchesDirty(false);
mInDisassemblyView = inDisassemblyView;
}
}
public bool ParseSourceFiles(BfSystem bfSystem, BfPassInstance passInstance, ProjectFolder projectFolder)
{
bool worked = true;
for (var item in projectFolder.mChildItems)
{
if (item is ProjectSource)
{
var projectSource = (ProjectSource)item;
worked &= ParseSourceFile(bfSystem, passInstance, projectSource);
}
if (item is ProjectFolder)
{
var innerProjectFolder = (ProjectFolder)item;
worked &= ParseSourceFiles(bfSystem, passInstance, innerProjectFolder);
}
}
return worked;
}
2019-08-23 11:56:54 -07:00
public bool HasPendingBeefFiles(bool forceQueue, ProjectFolder projectFolder)
{
2024-08-27 05:48:22 +02:00
bool hadBeef = false;
2019-08-23 11:56:54 -07:00
for (var item in projectFolder.mChildItems)
{
if (item.mIncludeKind == .Ignore)
continue;
2024-08-27 05:48:22 +02:00
if (item is ProjectSource)
{
var projectSource = (ProjectSource)item;
if (IsBeefFile(projectSource.mPath))
{
2019-08-23 11:56:54 -07:00
if ((projectSource.HasChangedSinceLastCompile) || (forceQueue))
{
hadBeef = true;
}
2024-08-27 05:48:22 +02:00
}
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if (item is ProjectFolder)
{
var innerProjectFolder = (ProjectFolder)item;
hadBeef |= HasPendingBeefFiles(forceQueue, innerProjectFolder);
}
2019-08-23 11:56:54 -07:00
}
return hadBeef;
}
2024-08-27 05:48:22 +02:00
public bool QueueParseBeefFiles(BfCompiler bfCompiler, bool forceQueue, ProjectFolder projectFolder, Project hotProject)
{
bool hadBeef = false;
for (var item in projectFolder.mChildItems)
{
2019-08-23 11:56:54 -07:00
if (item.mIncludeKind == .Ignore)
continue;
2024-08-27 05:48:22 +02:00
if (item is ProjectSource)
{
var projectSource = (ProjectSource)item;
if (IsBeefFile(projectSource.mPath))
{
2019-08-23 11:56:54 -07:00
// if it's the resolve compiler then we take this as an indication that we need to recompile this file
// later on when a build is requested, most likely because the project or workspace configuration has
// changed (such as preprocessor or other setting changes)
if ((bfCompiler == null) || (bfCompiler.mIsResolveOnly))
{
projectSource.HasChangedSinceLastCompile = true;
if (bfCompiler != null)
{
// Process change in resolve compiler
bfCompiler.QueueProjectSource(projectSource, .None, !bfCompiler.mIsResolveOnly);
}
2019-08-23 11:56:54 -07:00
}
else // Actual build
{
bool wantsHashRefresh = false;
if ((hotProject == null) && (projectSource.mWasBuiltWithOldHash))
wantsHashRefresh = true;
2024-08-27 05:48:22 +02:00
if ((projectSource.HasChangedSinceLastCompile) || (projectSource.mLoadFailed) || (forceQueue) || (wantsHashRefresh))
2019-08-23 11:56:54 -07:00
{
// mHasChangedSinceLastCompile is safe to set 'false' here since it just determines whether or not
// we rebuild the TypeDefs from the sources. It isn't affected by any compilation errors.
projectSource.mHasChangedSinceLastCompile = false;
projectSource.mWasBuiltWithOldHash = false;
SourceHash sourceHash = .None;
if (hotProject != null)
{
if (!mWorkspace.mCompileInstanceList.IsEmpty)
{
let compileInstance = mWorkspace.GetProjectSourceCompileInstance(projectSource, 0);
if (compileInstance != null)
sourceHash = compileInstance.mSourceHash;
}
}
2024-08-27 05:48:22 +02:00
bfCompiler.QueueProjectSource(projectSource, sourceHash, !bfCompiler.mIsResolveOnly);
2019-08-23 11:56:54 -07:00
hadBeef = true;
}
}
2024-08-27 05:48:22 +02:00
}
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if (item is ProjectFolder)
{
var innerProjectFolder = (ProjectFolder)item;
hadBeef |= QueueParseBeefFiles(bfCompiler, forceQueue, innerProjectFolder, hotProject);
}
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
return hadBeef;
}
2019-08-23 11:56:54 -07:00
#if IDE_C_SUPPORT
2024-08-27 05:48:22 +02:00
public void QueueParseClangFiles(ClangCompiler clangCompiler, ProjectFolder projectFolder)
{
for (var item in projectFolder.mChildItems)
{
if (item is ProjectSource)
{
var projectSource = (ProjectSource)item;
if (IsClangSourceFile(projectSource.mPath))
clangCompiler.QueueProjectSource(projectSource);
}
if (item is ProjectFolder)
{
var innerProjectFolder = (ProjectFolder)item;
QueueParseClangFiles(clangCompiler, innerProjectFolder);
}
}
}
2019-08-23 11:56:54 -07:00
#endif
2024-08-27 05:48:22 +02:00
public Workspace.Options GetCurWorkspaceOptions()
{
return mWorkspace.GetOptions(mConfigName, mPlatformName);
}
2019-08-23 11:56:54 -07:00
public Workspace.ConfigSelection GetCurConfigSelection(Project project)
{
var workspaceOptions = mWorkspace.GetOptions(mConfigName, mPlatformName);
if (workspaceOptions == null)
2024-08-27 05:48:22 +02:00
return null;
2019-08-23 11:56:54 -07:00
Workspace.ConfigSelection configSelection;
workspaceOptions.mConfigSelections.TryGetValue(project, out configSelection);
if ((configSelection == null) || (!configSelection.mEnabled))
2024-08-27 05:48:22 +02:00
return null;
2019-08-23 11:56:54 -07:00
return configSelection;
}
2024-08-27 05:48:22 +02:00
public Project.Options GetCurProjectOptions(Project project)
{
2019-08-23 11:56:54 -07:00
if (project.mFailed)
return null;
2024-08-27 05:48:22 +02:00
Workspace.ConfigSelection configSelection = GetCurConfigSelection(project);
if (configSelection == null)
return null;
return project.GetOptions(configSelection.mConfig, configSelection.mPlatform);
}
2019-08-23 11:56:54 -07:00
public bool IsProjectEnabled(Project project)
{
return GetCurProjectOptions(project) != null;
}
public bool IsProjectSourceEnabled(ProjectSource projectSource)
{
ProjectItem checkItem = projectSource;
while (checkItem != null)
{
if (checkItem.mIncludeKind == .Manual)
break;
if (checkItem.mIncludeKind == .Ignore)
return false;
checkItem = checkItem.mParentFolder;
}
2019-08-23 11:56:54 -07:00
if (!IsProjectEnabled(projectSource.mProject))
return false;
return true;
}
public void PreConfigureBeefSystem(BfSystem bfSystem, BfCompiler bfCompiler)
{
if (!bfCompiler.mIsResolveOnly)
{
bfCompiler.WaitForBackground();
bfSystem.ClearTypeOptions();
}
}
// Project options are inherently thread safe. Resolve-system project settings
2025-03-19 11:01:28 -04:00
// Can only be changed from the Resolve BfCompiler thread, and Build settings
// are only changed before background compilation begins. We also call this
2019-08-23 11:56:54 -07:00
// during WorkspaceLoad, but the resolve threads aren't processing then.
2024-08-27 05:48:22 +02:00
public bool SetupBeefProjectSettings(BfSystem bfSystem, BfCompiler bfCompiler, Project project)
{
bool success = true;
var bfProject = bfSystem.GetBfProject(project);
Project.Options options = GetCurProjectOptions(project);
Workspace.Options workspaceOptions = GetCurWorkspaceOptions();
if (options == null)
{
2024-12-31 14:15:12 -08:00
//Fail(scope String()..AppendF("Failed to retrieve options for {0}", project.mProjectName));
2024-08-27 05:48:22 +02:00
bfProject.SetDisabled(true);
return false;
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
bfProject.SetDisabled(false);
2019-08-23 11:56:54 -07:00
2022-01-28 08:19:11 -05:00
let platform = Workspace.PlatformType.GetFromName(mPlatformName, workspaceOptions.mTargetTriple);
2020-06-30 12:13:20 -07:00
2024-08-27 05:48:22 +02:00
var preprocessorMacros = scope DefinesSet();
2019-08-23 11:56:54 -07:00
void AddMacros(List<String> macros)
{
for (var macro in macros)
{
preprocessorMacros.Add(macro);
}
}
AddMacros(project.mBeefGlobalOptions.mPreprocessorMacros);
AddMacros(options.mBeefOptions.mPreprocessorMacros);
AddMacros(mWorkspace.mBeefGlobalOptions.mPreprocessorMacros);
AddMacros(workspaceOptions.mPreprocessorMacros);
GetBeefPreprocessorMacros(preprocessorMacros);
2024-08-27 05:48:22 +02:00
2019-08-23 11:56:54 -07:00
var optimizationLevel = workspaceOptions.mBfOptimizationLevel;
2024-08-27 05:48:22 +02:00
if (options.mBeefOptions.mOptimizationLevel != null)
optimizationLevel = options.mBeefOptions.mOptimizationLevel.Value;
2019-08-23 11:56:54 -07:00
2022-01-28 08:19:11 -05:00
bool isWin64 = true;
if (!workspaceOptions.mTargetTriple.IsWhiteSpace)
isWin64 = workspaceOptions.mTargetTriple.StartsWith("x86_64-pc-windows");
else
isWin64 = mPlatformName == "Win64";
2024-08-27 05:48:22 +02:00
2022-01-28 08:19:11 -05:00
if ((optimizationLevel == .OgPlus) && (!isWin64) && (bfCompiler == mBfBuildCompiler))
2019-08-23 11:56:54 -07:00
{
OutputLineSmart("WARNING: Project '{0}' has Og+ specified, which is only supported for Win64 targets.", project.mProjectName);
optimizationLevel = .O0;
}
2020-03-24 07:21:20 -07:00
BuildOptions.LTOType ltoType = .None;
if (!bfCompiler.mIsResolveOnly)
{
ltoType = workspaceOptions.mLTOType;
if (options.mBeefOptions.mLTOType != null)
ltoType = options.mBeefOptions.mLTOType.Value;
}
2024-08-27 05:48:22 +02:00
2019-08-23 11:56:54 -07:00
var targetType = project.mGeneralOptions.mTargetType;
if (bfSystem != mBfResolveSystem)
{
if (options.mBuildOptions.mBuildKind == .NotSupported)
{
OutputErrorLine("Project '{0}' is marked as 'not supported' for this platform/configuration", project.mProjectName);
success = false;
}
else if (options.mBuildOptions.mBuildKind == .Test)
2019-08-23 11:56:54 -07:00
{
if (workspaceOptions.mBuildKind == .Test)
{
targetType = .BeefTest;
if (mTestManager != null)
{
String workingDirRel = scope String();
ResolveConfigString(mPlatformName, workspaceOptions, project, options, "$(WorkingDir)", "debug working directory", workingDirRel);
var workingDir = scope String();
Path.GetAbsolutePath(workingDirRel, project.mProjectDir, workingDir);
mTestManager.AddProject(project, workingDir);
}
2019-08-23 11:56:54 -07:00
}
else
{
OutputErrorLine("Project '{0}' has a Test configuration specified but the workspace is not using a Test configuration", project.mProjectName);
2019-08-23 11:56:54 -07:00
success = false;
}
}
else if (options.mBuildOptions.mBuildKind == .StaticLib)
{
if (project.mGeneralOptions.mTargetType.IsBeefApplication)
targetType = .BeefApplication_StaticLib;
else if (project.mGeneralOptions.mTargetType == .BeefLib)
targetType = .BeefLib_Static;
}
else if (options.mBuildOptions.mBuildKind == .DynamicLib)
{
if (project.mGeneralOptions.mTargetType.IsBeefApplication)
targetType = .BeefApplication_DynamicLib;
else if (project.mGeneralOptions.mTargetType == .BeefLib)
targetType = .BeefLib_Dynamic;
}
2019-08-23 11:56:54 -07:00
}
2020-06-30 12:13:20 -07:00
var relocType = options.mBeefOptions.mRelocType;
if (relocType == .NotSet)
{
2020-08-06 09:24:37 -07:00
if ((platform != .Windows) && (platform != .Wasm))
2020-06-30 12:13:20 -07:00
relocType = .PIC;
}
2024-08-27 05:48:22 +02:00
bfProject.SetOptions(targetType,
project.mBeefGlobalOptions.mStartupObject,
preprocessorMacros.mDefines,
optimizationLevel, ltoType, relocType, options.mBeefOptions.mPICLevel,
options.mBeefOptions.mMergeFunctions, options.mBeefOptions.mCombineLoads,
2024-08-27 05:48:22 +02:00
options.mBeefOptions.mVectorizeLoops, options.mBeefOptions.mVectorizeSLP);
List<Project> depProjectList = scope List<Project>();
if (!GetDependentProjectList(project, depProjectList))
success = false;
bfProject.ClearDependencies();
for (var depProject in depProjectList)
{
if (bfSystem.mProjectMap.TryGetValue(depProject, var depBfProject))
bfProject.AddDependency(depBfProject);
}
2019-08-23 11:56:54 -07:00
if (!bfCompiler.mIsResolveOnly)
{
for (let typeOption in project.mBeefGlobalOptions.mDistinctBuildOptions)
bfSystem.AddTypeOptions(typeOption);
for (let typeOption in options.mBeefOptions.mDistinctBuildOptions)
bfSystem.AddTypeOptions(typeOption);
}
2024-08-27 05:48:22 +02:00
return success;
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public void CurrentWorkspaceConfigChanged()
{
2019-08-23 11:56:54 -07:00
#if IDE_C_SUPPORT
for (var val in mDepClang.mProjectBuildString.Values)
delete val;
mDepClang.mProjectBuildString.Clear();
#endif
if (mBfResolveCompiler != null)
{
2024-08-27 05:48:22 +02:00
mBfResolveCompiler.QueueSetWorkspaceOptions(null, 0);
mBfResolveCompiler.QueueDeferredResolveAll();
2019-08-23 11:56:54 -07:00
}
2024-08-27 05:48:22 +02:00
mWorkspace.FixOptions(mConfigName, mPlatformName);
for (var project in mWorkspace.mProjects)
{
ProjectOptionsChanged(project);
2019-08-23 11:56:54 -07:00
#if IDE_C_SUPPORT
2024-08-27 05:48:22 +02:00
QueueParseClangFiles(mDepClang, project.mRootFolder);
2019-08-23 11:56:54 -07:00
#endif
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
mWorkspace.ClearProjectNameCache();
2024-10-21 09:18:07 -04:00
mProjectPanel?.RehupProjects();
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
/*public string GetClangDepConfigName(Project project)
{
string[] clangArgs = GetClangResolveArgs(project);
string clangArgsStr = String.Join("\n", clangArgs);
long hash = 0;
2025-03-19 11:01:28 -04:00
for (int i = 0; i < clangArgsStr.Length; i++)
2019-08-23 11:56:54 -07:00
hash = (hash << 5) - hash + clangArgsStr[i];
return String.Format("{0:X16}", hash);
}*/
2024-08-27 05:48:22 +02:00
public void ProjectOptionsChanged(Project project, bool reparseFiles = true)
{
2019-08-23 11:56:54 -07:00
bool isEnabled = IsProjectEnabled(project);
mWorkspace.ClearProjectNameCache();
if (mBfResolveCompiler != null)
{
2024-08-27 05:48:22 +02:00
mBfResolveCompiler.QueueSetupProjectSettings(project);
2019-08-23 11:56:54 -07:00
}
if (isEnabled != project.mEnabled)
{
project.mEnabled = isEnabled;
if (isEnabled)
{
QueueProjectItems(project);
}
else
{
RemoveProjectItems(project);
}
2024-10-21 09:18:07 -04:00
mBfResolveCompiler?.QueueDeferredResolveAll();
mBfResolveCompiler?.QueueRefreshViewCommand(.FullRefresh);
2019-08-23 11:56:54 -07:00
return;
}
#if IDE_C_SUPPORT
2024-08-27 05:48:22 +02:00
mDepClang.mDoDependencyCheck = true;
2019-08-23 11:56:54 -07:00
#endif
if (mBfResolveCompiler != null)
{
2020-03-09 11:47:09 -07:00
if (IsProjectEnabled(project))
{
2024-08-27 05:48:22 +02:00
if (reparseFiles)
QueueParseBeefFiles(mBfResolveCompiler, false, project.mRootFolder, null);
mBfResolveCompiler.QueueDeferredResolveAll();
mBfResolveCompiler.QueueRefreshViewCommand();
2020-03-09 11:47:09 -07:00
}
2019-08-23 11:56:54 -07:00
}
else
{
if (reparseFiles)
QueueParseBeefFiles(mBfResolveCompiler, false, project.mRootFolder, null);
2019-08-23 11:56:54 -07:00
}
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
public void RenameProject(Project project, String newName)
{
mWantsBeefClean = true;
2024-10-21 09:18:07 -04:00
var checkDeclName = (project.mProjectName !== project.mProjectNameDecl) && (mWorkspace.FindProject(project.mProjectNameDecl) == project);
2019-08-23 11:56:54 -07:00
for (var checkProject in mWorkspace.mProjects)
{
for (var dep in checkProject.mDependencies)
{
2024-10-21 09:18:07 -04:00
if ((dep.mProjectName == project.mProjectName) ||
((checkDeclName) && (dep.mProjectName == project.mProjectNameDecl)))
2019-08-23 11:56:54 -07:00
{
dep.mProjectName.Set(newName);
checkProject.SetChanged();
}
}
}
for (var dep in mWorkspace.mProjectSpecs)
{
if (dep.mProjectName == project.mProjectName)
{
dep.mProjectName.Set(newName);
mWorkspace.SetChanged();
}
}
project.mProjectName.Set(newName);
2024-10-21 09:18:07 -04:00
if (project.mProjectNameDecl != project.mProjectName)
delete project.mProjectNameDecl;
project.mProjectNameDecl = project.mProjectName;
2019-08-23 11:56:54 -07:00
project.SetChanged();
mWorkspace.ClearProjectNameCache();
2024-10-21 09:18:07 -04:00
mProjectPanel.RebuildUI();
2019-08-23 11:56:54 -07:00
}
2024-08-27 05:48:22 +02:00
public void RemoveProject(Project project)
{
2019-08-23 11:56:54 -07:00
RemoveProjectItems(project);
2024-10-21 09:18:07 -04:00
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;
2024-08-27 05:48:22 +02:00
mWorkspace.SetChanged();
mWorkspace.mProjects.Remove(project);
2019-08-23 11:56:54 -07:00
#if IDE_C_SUPPORT
2024-08-27 05:48:22 +02:00
using (mDepClang.mMonitor.Enter())
{
mDepClang.mProjectBuildString.Remove(project);
}
2019-08-23 11:56:54 -07:00
#endif
2024-08-27 05:48:22 +02:00
if (mWorkspace.mStartupProject == project)
mWorkspace.mStartupProject = null;
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
mWorkspace.FixOptions();
CurrentWorkspaceConfigChanged();
2019-08-23 11:56:54 -07:00
var bfCompilers = scope List<BfCompiler>();
GetBfCompilers(bfCompilers);
2024-08-27 05:48:22 +02:00
for (var bfCompiler in bfCompilers)
{
var bfProject = bfCompiler.mBfSystem.GetBfProject(project);
bfProject.SetDisabled(true);
bfCompiler.mBfSystem.RemoveBfProject(project);
bfCompiler.QueueDeleteBfProject(bfProject);
}
2019-08-23 11:56:54 -07:00
for (var checkProject in mWorkspace.mProjects)
{
for (var dep in checkProject.mDependencies)
{
if (dep.mProjectName == project.mProjectName)
{
checkProject.SetChanged();
2019-08-23 11:56:54 -07:00
@dep.Remove();
delete dep;
}
}
}
for (var dep in mWorkspace.mProjectSpecs)
{
if (dep.mProjectName == project.mProjectName)
{
@dep.Remove();
delete dep;
}
}
List<WidgetWindow> closeList = scope .();
for (var window in gApp.mWindows)
{
if (var widgetWindow = window as WidgetWindow)
{
if (var projectProperties = widgetWindow.mRootWidget as ProjectProperties)
{
if (projectProperties.[Friend]mProject == project)
closeList.Add(widgetWindow);
}
}
}
for (var window in closeList)
window.Close(true);
2020-04-20 07:00:02 -07:00
IdleDeferDelete(project);
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
BfPassInstance CompileBeef(Project hotProject, int32 hotIdx, bool lastCompileHadMessages, out bool hadBeef)
{
CompilerLog("IDEApp.CompileBeef");
2019-08-23 11:56:54 -07:00
hadBeef = false;
mCompilingBeef = true;
BeefCompileStarted();
2024-08-27 05:48:22 +02:00
bool success = true;
BfSystem bfSystem = mBfBuildSystem;
BfCompiler bfCompiler = mBfBuildCompiler;
BfPassInstance passInstance = bfSystem.CreatePassInstance();
bfCompiler.QueueSetPassInstance(passInstance);
bfCompiler.QueueSetWorkspaceOptions(hotProject, hotIdx);
2019-08-23 11:56:54 -07:00
Workspace.Options workspaceOptions = GetCurWorkspaceOptions();
bool tryQueueFiles = true;
bool doCompile = false;
if (lastCompileHadMessages)
doCompile = true;
2021-12-20 09:52:29 -05:00
bool needsComptime = bfCompiler.GetLastHadComptimeRebuilds();
2022-03-08 06:27:06 -08:00
if (mDebugger?.mIsComptimeDebug == true)
needsComptime = true;
2019-08-23 11:56:54 -07:00
if ((!workspaceOptions.mIncrementalBuild) && (!lastCompileHadMessages))
{
tryQueueFiles = false;
for (var project in mWorkspace.mProjects)
{
if (HasPendingBeefFiles(false, project.mRootFolder))
tryQueueFiles = true;
}
}
if (needsComptime)
tryQueueFiles = true;
2019-08-23 11:56:54 -07:00
if (hotProject != null)
{
mWorkspace.mHadHotCompileSinceLastFullCompile = true;
}
else
{
if (mWorkspace.mHadHotCompileSinceLastFullCompile)
{
doCompile = true;
mWorkspace.mHadHotCompileSinceLastFullCompile = false;
}
}
if (mWorkspace.mForceNextCompile)
{
doCompile = true;
mWorkspace.mForceNextCompile = false;
}
if (tryQueueFiles)
{
PreConfigureBeefSystem(bfSystem, bfCompiler);
2024-08-27 05:48:22 +02:00
for (var project in mWorkspace.mProjects)
{
if (SetupBeefProjectSettings(bfSystem, bfCompiler, project))
{
doCompile |= QueueParseBeefFiles(bfCompiler, !workspaceOptions.mIncrementalBuild, project.mRootFolder, hotProject);
}
else if (IsProjectEnabled(project))
success = false;
}
2019-08-23 11:56:54 -07:00
}
if (needsComptime)
doCompile = true;
2024-08-27 05:48:22 +02:00
if (!success)
{
bfCompiler.QueueDeletePassInstance(passInstance);
2024-08-27 05:48:22 +02:00
return null;
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
for (var project in mWorkspace.mProjects)
{
2019-08-23 11:56:54 -07:00
if (!project.mGeneralOptions.mTargetType.IsBeef)
continue;
hadBeef = true;
2024-08-27 05:48:22 +02:00
String projectBuildDir = scope String();
GetWorkspaceBuildDir(projectBuildDir);
projectBuildDir.Append("/", project.mProjectName);
Directory.CreateDirectory(projectBuildDir).IgnoreError();
}
2019-08-23 11:56:54 -07:00
if (!hadBeef)
doCompile = false;
2024-08-27 05:48:22 +02:00
if (doCompile)
{
2019-10-05 10:24:58 -07:00
for (var project in mWorkspace.mProjects)
{
// Regenerate these
DeleteContainerAndItems!(project.mCurBfOutputFileNames);
project.mCurBfOutputFileNames = null;
}
2019-08-23 11:56:54 -07:00
var dir = scope String();
GetWorkspaceBuildDir(dir);
2024-08-27 05:48:22 +02:00
bfCompiler.QueueCompile(dir);
}
else
{
2019-08-23 11:56:54 -07:00
bfCompiler.ClearResults();
2024-08-27 05:48:22 +02:00
passInstance.mCompileSucceeded = true;
}
return passInstance;
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public bool DoResolveConfigString(String platformName, Workspace.Options workspaceOptions, Project project, Project.Options options, StringView configString, String error, String result)
{
2021-02-25 10:14:22 -08:00
int startIdx = result.Length;
int i = startIdx;
2019-08-23 11:56:54 -07:00
result.Append(configString);
bool hadError = false;
2024-08-27 05:48:22 +02:00
for (; i < result.Length - 2; i++)
{
if ((result[i] == '$') && (result[i + 1] == '('))
{
int parenPos = -1;
int openCount = 1;
bool inString = false;
char8 prevC = 0;
for (int checkIdx = i + 2; checkIdx < result.Length; checkIdx++)
{
char8 c = result[checkIdx];
if (inString)
{
if (prevC == '\\')
{
// Slashed char
prevC = 0;
continue;
}
if (c == '"')
inString = false;
}
else
{
if (c == '"')
inString = true;
else if (c == '(')
openCount++;
else if (c == ')')
{
openCount--;
if (openCount == 0)
{
parenPos = checkIdx;
break;
2024-08-27 05:48:22 +02:00
}
}
}
prevC = c;
}
2024-08-27 05:48:22 +02:00
if (parenPos != -1)
ReplaceBlock:
do
{
2024-08-27 05:48:22 +02:00
String replaceStr = scope String(result, i + 2, parenPos - i - 2);
String newString = null;
2024-08-27 05:48:22 +02:00
if (replaceStr.Contains(' '))
{
2024-08-27 05:48:22 +02:00
String cmd = scope .();
List<String> args = scope .();
for (let str in replaceStr.Split(' ', .RemoveEmptyEntries))
{
2024-08-27 05:48:22 +02:00
if (cmd.IsEmpty)
cmd.Set(str);
else
{
2024-08-27 05:48:22 +02:00
String arg = scope:ReplaceBlock .();
if (str.StartsWith("\""))
{
String unresolvedStr = scope .();
str.UnQuoteString(unresolvedStr);
if (!DoResolveConfigString(platformName, workspaceOptions, project, options, unresolvedStr, error, arg))
return false;
}
else
arg.Append(str);
args.Add(arg);
}
}
2024-08-27 05:48:22 +02:00
String cmdErr = null;
2024-08-27 05:48:22 +02:00
switch (cmd)
{
2024-08-27 05:48:22 +02:00
case "Slash":
if (args.Count == 1)
{
newString = scope:ReplaceBlock .();
args[0].Quote(newString);
}
else
cmdErr = "Invalid number of arguments";
case "Var":
break ReplaceBlock;
case "Arguments",
"BuildDir",
"LinkFlags",
"ProjectDir",
"ProjectName",
"TargetDir",
"TargetPath",
"WorkingDir":
var selProject = mWorkspace.FindProject(args[0]);
if (selProject != null)
{
Workspace.Options selWorkspaceOptions = gApp.GetCurWorkspaceOptions();
Project.Options selOptions = gApp.GetCurProjectOptions(selProject);
String selConfigString = scope $"$({cmd})";
replaceStr.Clear();
newString = scope:ReplaceBlock .();
DoResolveConfigString(platformName, selWorkspaceOptions, selProject, selOptions, selConfigString, error, newString);
}
else
cmdErr = "Unable to find project";
default:
cmdErr = "Invalid command";
}
2024-08-27 05:48:22 +02:00
if (newString == null)
2021-02-25 10:14:22 -08:00
{
2024-08-27 05:48:22 +02:00
if (error != null)
{
if (cmdErr != null)
error.Set(cmdErr);
else
error.Set(replaceStr);
}
hadError = true;
break ReplaceBlock;
2021-02-25 10:14:22 -08:00
}
}
2024-08-27 05:48:22 +02:00
if ((newString == null) && (project != null))
{
2024-08-27 05:48:22 +02:00
switch (replaceStr)
{
2024-08-27 05:48:22 +02:00
case "ProjectName":
newString = project.mProjectName;
break;
}
2019-08-23 11:56:54 -07:00
}
2024-08-27 05:48:22 +02:00
if ((newString == null) && (options != null))
2019-08-23 11:56:54 -07:00
{
2024-08-27 05:48:22 +02:00
switch (replaceStr)
{
2024-08-27 05:48:22 +02:00
case "Arguments":
if (mLaunchData?.mArgs != null)
newString = mLaunchData.mArgs;
else
newString = options.mDebugOptions.mCommandArguments;
case "WorkingDir":
if (mLaunchData?.mWorkingDir != null)
newString = mLaunchData.mWorkingDir;
else
newString = options.mDebugOptions.mWorkingDirectory;
IDEUtils.FixFilePath(newString);
case "TargetDir":
{
2024-08-27 05:48:22 +02:00
if (project.IsDebugSession)
{
let targetPath = scope:ReplaceBlock String();
DoResolveConfigString(platformName, workspaceOptions, project, options, options.mBuildOptions.mTargetName, error, targetPath);
newString = scope:ReplaceBlock String();
Path.GetDirectoryPath(targetPath, newString);
break;
}
String targetDir = scope String();
DoResolveConfigString(platformName, workspaceOptions, project, options, options.mBuildOptions.mTargetDirectory, error, targetDir);
newString = scope:ReplaceBlock String();
2024-08-27 05:48:22 +02:00
Path.GetAbsolutePath(targetDir, project.mProjectDir, newString);
IDEUtils.FixFilePath(newString);
}
2024-08-27 05:48:22 +02:00
case "TargetPath":
2019-08-23 11:56:54 -07:00
{
2024-08-27 05:48:22 +02:00
if (project.IsDebugSession)
{
newString = scope:ReplaceBlock String();
DoResolveConfigString(platformName, workspaceOptions, project, options, options.mBuildOptions.mTargetName, error, newString);
break;
}
String targetDir = scope String();
DoResolveConfigString(platformName, workspaceOptions, project, options, options.mBuildOptions.mTargetDirectory, error, targetDir);
2019-08-23 11:56:54 -07:00
newString = scope:ReplaceBlock String();
2024-08-27 05:48:22 +02:00
Path.GetAbsolutePath(targetDir, project.mProjectDir, newString);
Utils.GetDirWithSlash(newString);
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if (!DoResolveConfigString(platformName, workspaceOptions, project, options, options.mBuildOptions.mTargetName, error, newString))
return false;
2024-08-27 05:48:22 +02:00
let platformType = Workspace.PlatformType.GetFromName(platformName, workspaceOptions.mTargetTriple);
2024-08-27 05:48:22 +02:00
switch (platformType)
{
case .Windows:
if (options.mBuildOptions.mBuildKind == .DynamicLib)
newString.Append(".dll");
else if ((options.mBuildOptions.mBuildKind == .StaticLib) || (project.mGeneralOptions.mTargetType == .BeefLib))
newString.Append(".lib");
else if (project.mGeneralOptions.mTargetType != .CustomBuild)
newString.Append(".exe");
case .macOS:
if (options.mBuildOptions.mBuildKind == .DynamicLib)
newString.Append(".dylib");
else if (options.mBuildOptions.mBuildKind == .StaticLib)
newString.Append(".a");
case .Wasm:
if (!newString.Contains('.'))
newString.Append(".html");
default:
if (options.mBuildOptions.mBuildKind == .DynamicLib)
newString.Append(".so");
else if (options.mBuildOptions.mBuildKind == .StaticLib)
newString.Append(".a");
}
}
IDEUtils.FixFilePath(newString);
case "ProjectDir":
if (project.IsDebugSession)
{
2024-08-27 05:48:22 +02:00
newString = scope:ReplaceBlock String();
Path.GetDirectoryPath(project.mProjectPath, newString);
}
2024-08-27 05:48:22 +02:00
else
newString = project.mProjectDir;
IDEUtils.FixFilePath(newString);
case "BuildDir":
newString = scope:ReplaceBlock String();
2024-08-27 05:48:22 +02:00
GetProjectBuildDir(project, newString);
IDEUtils.FixFilePath(newString);
//Debug.WriteLine("BuildDir: {0}", newString);
case "LinkFlags":
newString = scope:ReplaceBlock String();
bool isBeefDynLib = (project.mGeneralOptions.mTargetType == .BeefLib) && (options.mBuildOptions.mBuildKind == .DynamicLib);
2024-08-27 05:48:22 +02:00
if ((project.mGeneralOptions.mTargetType == .BeefConsoleApplication) ||
(project.mGeneralOptions.mTargetType == .BeefGUIApplication) ||
(isBeefDynLib) ||
(options.mBuildOptions.mBuildKind == .Test))
{
2024-08-27 05:48:22 +02:00
let platformType = Workspace.PlatformType.GetFromName(platformName, workspaceOptions.mTargetTriple);
String rtName = scope String();
String dbgName = scope String();
String allocName = scope String();
BuildContext.GetRtLibNames(platformType, workspaceOptions, options, false, rtName, dbgName, allocName);
2024-08-27 05:48:22 +02:00
switch (platformType)
{
case .Windows:
newString.Append(rtName);
if (!dbgName.IsEmpty)
newString.Append(" ", dbgName);
if (!allocName.IsEmpty)
newString.Append(" ", allocName);
case .macOS:
newString.AppendF("./{} -Wl,-rpath -Wl,@executable_path", rtName);
case .iOS:
case .Linux:
newString.AppendF("./{} -lpthread -ldl -Wl,-rpath -Wl,$ORIGIN", rtName);
case .Wasm:
newString.Append("\"");
newString.Append(mInstallDir);
newString.Append("Beef", IDEApp.sRTVersionStr, "RT");
newString.Append((Workspace.PlatformType.GetPtrSizeByName(gApp.mPlatformName) == 4) ? "32" : "64");
newString.Append("_wasm");
if (project.mWasmOptions.mEnableThreads)
newString.Append("_pthread");
newString.Append(".a\"");
default:
}
}
case "VSToolPath":
if (Workspace.PlatformType.GetPtrSizeByName(platformName) == 4)
newString = gApp.mSettings.mVSSettings.mBin32Path;
else
newString = gApp.mSettings.mVSSettings.mBin64Path;
IDEUtils.FixFilePath(newString);
case "VSToolPath_x86":
2019-08-23 11:56:54 -07:00
newString = gApp.mSettings.mVSSettings.mBin32Path;
2024-08-27 05:48:22 +02:00
IDEUtils.FixFilePath(newString);
case "VSToolPath_x64":
2019-08-23 11:56:54 -07:00
newString = gApp.mSettings.mVSSettings.mBin64Path;
2024-08-27 05:48:22 +02:00
IDEUtils.FixFilePath(newString);
case "EmccPath":
newString = scope:ReplaceBlock String();
newString.AppendF($"{gApp.mSettings.mEmscriptenPath}/upstream/emscripten/emcc.bat");
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
}
2024-08-27 05:48:22 +02:00
if ((newString == null) && (mScriptManager != null))
2019-08-23 11:56:54 -07:00
{
2024-08-27 05:48:22 +02:00
switch (replaceStr)
2019-08-23 11:56:54 -07:00
{
2024-08-27 05:48:22 +02:00
case "ScriptDir":
if ((mScriptManager != null) && (mScriptManager.mCurCmd != null))
{
newString = scope:ReplaceBlock String();
Path.GetDirectoryPath(mScriptManager.mCurCmd.mSrcFile, newString);
}
2019-08-23 11:56:54 -07:00
}
}
2024-08-27 05:48:22 +02:00
if (newString == null)
{
switch (replaceStr)
{
2024-08-27 05:48:22 +02:00
case "Configuration":
newString = mConfigName;
case "Platform":
newString = mPlatformName;
case "WorkspaceDir":
if (mWorkspace.mDir != null)
newString = mWorkspace.mDir;
else if (project.IsDebugSession)
{
newString = scope:ReplaceBlock String();
Path.GetDirectoryPath(project.mProjectPath, newString);
}
case "BeefPath":
newString = gApp.mInstallDir;
default:
// Check if any custom properties match the string.
if (CustomBuildProperties.Contains(replaceStr))
{
newString = scope:ReplaceBlock String();
newString.Append(CustomBuildProperties.Get(replaceStr));
}
}
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if (newString == null)
{
if (error != null)
error.Set(replaceStr);
hadError = true;
break;
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if (newString != null)
{
result.Remove(i, parenPos - i + 1);
result.Insert(i, newString);
i--;
}
2019-08-23 11:56:54 -07:00
}
2024-08-27 05:48:22 +02:00
}
}
2019-08-23 11:56:54 -07:00
return !hadError;
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
public void ReplaceVariables(String result)
{
int i = 0;
2024-08-27 05:48:22 +02:00
for (; i < result.Length - 2; i++)
{
2024-08-27 05:48:22 +02:00
if ((result[i] == '$') && (result[i + 1] == '('))
{
int parenPos = -1;
int openCount = 1;
bool inString = false;
char8 prevC = 0;
for (int checkIdx = i + 2; checkIdx < result.Length; checkIdx++)
{
char8 c = result[checkIdx];
if (inString)
{
if (prevC == '\\')
{
// Slashed char
prevC = 0;
continue;
}
if (c == '"')
inString = false;
}
else
{
if (c == '"')
inString = true;
else if (c == '(')
openCount++;
else if (c == ')')
{
openCount--;
if (openCount == 0)
{
parenPos = checkIdx;
break;
2024-08-27 05:48:22 +02:00
}
}
}
prevC = c;
}
2024-08-27 05:48:22 +02:00
if (parenPos != -1)
ReplaceBlock:
do
{
String replaceStr = scope String(result, i + 2, parenPos - i - 2);
2024-08-27 05:48:22 +02:00
if (!replaceStr.StartsWith("Var "))
continue;
2024-08-27 05:48:22 +02:00
String varName = scope String(replaceStr, 4);
String newString = null;
2024-08-27 05:48:22 +02:00
if (mScriptManager.mContext.mVars.TryGetValue(varName, var value))
{
2024-08-27 05:48:22 +02:00
if (value.VariantType == typeof(String))
{
newString = scope:ReplaceBlock String(value.Get<String>());
}
}
2024-08-27 05:48:22 +02:00
if (newString == null)
{
2024-08-27 05:48:22 +02:00
if (mBuildContext != null)
{
2024-08-27 05:48:22 +02:00
if (mBuildContext.mScriptContext.mVars.TryGetValue(varName, out value))
{
2024-08-27 05:48:22 +02:00
if (value.VariantType == typeof(String))
{
newString = scope:ReplaceBlock String(value.Get<String>());
}
}
}
}
2024-08-27 05:48:22 +02:00
if (newString != null)
{
result.Remove(i, parenPos - i + 1);
result.Insert(i, newString);
i--;
}
}
2024-08-27 05:48:22 +02:00
}
}
}
2024-08-27 05:48:22 +02:00
public bool ResolveConfigString(String platformName, Workspace.Options workspaceOptions, Project project, Project.Options options, StringView configString, String errorContext, String outResult)
{
String errorString = scope String();
if (!DoResolveConfigString(platformName, workspaceOptions, project, options, configString, errorString, outResult))
2019-08-23 11:56:54 -07:00
{
OutputErrorLine("Invalid macro in {0}: {1}", errorContext, errorString);
2019-08-23 11:56:54 -07:00
return false;
}
2024-08-27 05:48:22 +02:00
return true;
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public void GetWorkspaceBuildDir(String outResult)
{
2019-08-23 11:56:54 -07:00
if (mWorkspace.mDir == null)
return;
2024-08-27 05:48:22 +02:00
outResult.Append(mWorkspace.mDir, "/build/", mConfigName, "_", mPlatformName);
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public void GetProjectBuildDir(Project project, String outResult)
{
GetWorkspaceBuildDir(outResult);
2019-08-23 11:56:54 -07:00
if (!outResult.IsEmpty)
2024-08-27 05:48:22 +02:00
outResult.Append("/", project.mProjectName);
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public void GetClangOutputFilePathWithoutExtension(ProjectSource projectSource, String outResult)
{
GetProjectBuildDir(projectSource.mProject, outResult);
outResult.Append("/");
2019-08-23 11:56:54 -07:00
String fullPath = scope String();
projectSource.GetFullImportPath(fullPath);
2024-08-27 05:48:22 +02:00
Path.GetFileNameWithoutExtension(fullPath, outResult);
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public void GetClangBuildString(Project project, Project.Options options, Workspace.Options workspaceOptions, bool isC, String clangOptions)
{
bool isClang = options.mCOptions.mCompilerType == Project.CCompilerType.Clang;
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if (options.mCOptions.mEmitDebugInfo)
{
if (isClang)
2019-08-23 11:56:54 -07:00
{
2024-08-27 05:48:22 +02:00
clangOptions.Append("-g -fstandalone-debug ");
2019-08-23 11:56:54 -07:00
if (workspaceOptions.mToolsetType != .GNU)
clangOptions.Append("-gcodeview ");
}
2024-08-27 05:48:22 +02:00
else
clangOptions.Append("-g ");
}
2019-08-23 11:56:54 -07:00
//TODO:
2024-08-27 05:48:22 +02:00
var simd = workspaceOptions.mCSIMDSetting;
if (options.mCOptions.mSIMD != null)
simd = options.mCOptions.mSIMD.Value;
switch (simd)
{
case .SSE:
clangOptions.Append("-msse ");
break;
case .SSE2:
clangOptions.Append("-msse2 ");
break;
case .SSE3:
clangOptions.Append("-msse3 ");
break;
case .SSE4:
clangOptions.Append("-msse4 ");
break;
case .SSE41:
clangOptions.Append("-msse4.1 ");
break;
case .AVX:
clangOptions.Append("-mavx ");
break;
case .AVX2:
clangOptions.Append("-mavx2 ");
break;
2019-08-23 11:56:54 -07:00
default:
2024-08-27 05:48:22 +02:00
}
if (options.mCOptions.mEnableBeefInterop)
{
var beefPreprocMacros = scope DefinesSet();
GetBeefPreprocessorMacros(beefPreprocMacros);
for (var beefPreprocMacro in beefPreprocMacros.mDefines)
clangOptions.Append("-D", beefPreprocMacro, " ");
}
if (options.mCOptions.mAllWarnings)
clangOptions.Append("-Wall ");
var optimizationLevel = options.mCOptions.mOptimizationLevel;
if (optimizationLevel == Project.COptimizationLevel.FromWorkspace)
optimizationLevel = (Project.COptimizationLevel)workspaceOptions.mCOptimizationLevel;
clangOptions.AppendF("-{0} ", optimizationLevel);
for (var preprocMacro in options.mCOptions.mPreprocessorMacros)
{
clangOptions.Append("-D", preprocMacro, " ");
}
for (var includePath in options.mCOptions.mIncludePaths)
{
2019-08-23 11:56:54 -07:00
var fullIncludePath = scope String();
project.GetProjectFullPath(includePath, fullIncludePath);
2024-08-27 05:48:22 +02:00
if (fullIncludePath.Contains(' '))
clangOptions.Append("\"-I", fullIncludePath, "\" ");
else
clangOptions.Append("-I", fullIncludePath, " ");
}
if (options.mCOptions.mCompilerType == Project.CCompilerType.GCC)
{
if (Workspace.PlatformType.GetPtrSizeByName(gApp.mPlatformName) == 4)
clangOptions.Append("-m32 ");
else
clangOptions.Append("-m64 ");
}
else
{
2019-08-23 11:56:54 -07:00
clangOptions.Append("--target=");
if (TargetTriple.IsTargetTriple(gApp.mPlatformName))
clangOptions.Append(gApp.mPlatformName);
else
2024-08-27 05:48:22 +02:00
Workspace.PlatformType.GetTargetTripleByName(gApp.mPlatformName, workspaceOptions.mToolsetType, clangOptions);
2019-08-23 11:56:54 -07:00
clangOptions.Append(" ");
if (workspaceOptions.mToolsetType == .GNU)
{
//
}
2024-08-27 05:48:22 +02:00
else
2019-08-23 11:56:54 -07:00
{
clangOptions.Append("-I\"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\include\" ");
clangOptions.Append("-I\"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\atlmfc\\include\" ");
clangOptions.Append("-I\"C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.14393.0\\ucrt\" ");
clangOptions.Append("-I\"C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.14393.0\\um\" ");
clangOptions.Append("-I\"C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.14393.0\\shared\" ");
clangOptions.Append("-I\"C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.14393.0\\winrt\" ");
}
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if (options.mCOptions.mNoOmitFramePointers)
clangOptions.Append("-fno-omit-frame-pointer ");
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if (options.mCOptions.mGenerateLLVMAsm)
clangOptions.Append("-emit-llvm -S ");
else
clangOptions.Append("-c ");
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if (!isC)
{
if (!String.IsNullOrEmpty(options.mCOptions.mOtherCPPFlags))
clangOptions.Append(options.mCOptions.mOtherCPPFlags, " ");
clangOptions.Append("-std=c++14 ");
}
else
{
if (!String.IsNullOrEmpty(options.mCOptions.mOtherCFlags))
clangOptions.Append(options.mCOptions.mOtherCFlags, " ");
}
}
2019-08-23 11:56:54 -07:00
2024-10-21 09:18:07 -04:00
protected static void QuoteIfNeeded(String str)
2024-08-27 05:48:22 +02:00
{
if (!str.Contains(' '))
return;
2019-08-23 11:56:54 -07:00
for (int32 i = 0; i < str.Length; i++)
{
char8 c = str[i];
if ((c == '\\') || (c == '"'))
{
str.Insert(i, '\\');
i++;
}
}
str.Insert(0, '"');
str.Append('"');
2024-08-27 05:48:22 +02:00
}
ExecutionQueueCmd CompileSource(Project project, Workspace.Options workspaceOptions, Project.Options options, String buildFileName, String addOptions = null)
{
String workspaceBuildDir = scope String();
GetWorkspaceBuildDir(workspaceBuildDir);
String projectBuildDir = scope String(workspaceBuildDir, "/", project.mProjectName);
String baseName = scope String();
Path.GetFileNameWithoutExtension(buildFileName, baseName);
String objName = scope String(projectBuildDir, "/", baseName, (options.mCOptions.mGenerateLLVMAsm ? ".ll" : ".obj"));
String llvmDir = scope String(IDEApp.sApp.mInstallDir);
IDEUtils.FixFilePath(llvmDir);
llvmDir.Append("llvm/");
String gccExePath = "c:/mingw/bin/g++.exe";
String clangCppExePath = scope String(llvmDir, "bin/clang++.exe");
2019-08-23 11:56:54 -07:00
String clangCExePath = scope String(llvmDir, "bin/clang.exe");
2024-08-27 05:48:22 +02:00
String clangOptions = scope String(buildFileName);
QuoteIfNeeded(clangOptions);
clangOptions.Append(" ");
bool isC = false;
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
var ext = scope String();
Path.GetExtension(buildFileName, ext);
if (ext == ".c")
isC = true;
2019-08-23 11:56:54 -07:00
var buildStr = scope String();
GetClangBuildString(project, options, workspaceOptions, isC, buildStr);
2024-08-27 05:48:22 +02:00
clangOptions.Append(buildStr);
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
int lastDot = objName.LastIndexOf('.');
2019-08-23 11:56:54 -07:00
String depFileName = scope String(objName, 0, lastDot);
2024-08-27 05:48:22 +02:00
QuoteIfNeeded(depFileName);
depFileName.Append(".dep");
clangOptions.Append("-MD -MF ", depFileName, " ");
2019-08-23 11:56:54 -07:00
if (addOptions != null)
clangOptions.Append(addOptions, " ");
2024-08-27 05:48:22 +02:00
clangOptions.Append("-o ");
2019-08-23 11:56:54 -07:00
String quotedObjName = scope String(objName);
2024-08-27 05:48:22 +02:00
QuoteIfNeeded(quotedObjName);
2019-08-23 11:56:54 -07:00
clangOptions.Append(quotedObjName);
2024-08-27 05:48:22 +02:00
String compilerExePath = (options.mCOptions.mCompilerType == Project.CCompilerType.GCC) ? gccExePath : isC ? clangCExePath : clangCppExePath;
return QueueRun(compilerExePath, clangOptions, IDEApp.sApp.mInstallDir, .UTF8);
}
2019-08-23 11:56:54 -07:00
void SkipProjectCompile(Project project, Project hotProject)
{
2024-08-27 05:48:22 +02:00
Project.Options options = GetCurProjectOptions(project);
if (options == null)
return;
BfCompiler bfCompiler = mBfBuildCompiler;
2022-05-13 13:24:32 -07:00
BfSystem bfSystem = mBfBuildSystem;
2024-08-27 05:48:22 +02:00
var bfProject = mBfBuildSystem.mProjectMap[project];
bool bfHadOutputChanges;
List<String> bfFileNames = scope List<String>();
2022-05-13 13:24:32 -07:00
bfSystem.Lock(0);
2022-01-28 08:19:11 -05:00
bfCompiler.GetOutputFileNames(bfProject, .None, out bfHadOutputChanges, bfFileNames);
2022-05-13 13:24:32 -07:00
bfSystem.Unlock();
defer ClearAndDeleteItems(bfFileNames);
2024-08-27 05:48:22 +02:00
if (bfHadOutputChanges)
project.mNeedsTargetRebuild = true;
2019-08-23 11:56:54 -07:00
}
void GetTargetPaths(Project project, String platformName, Workspace.Options workspaceOptions, Project.Options options, List<String> outPaths)
2019-08-23 11:56:54 -07:00
{
String targetPath = scope String();
ResolveConfigString(platformName, workspaceOptions, project, options, "$(TargetPath)", "Target path", targetPath);
2019-08-23 11:56:54 -07:00
outPaths.Add(new String(targetPath));
#if BF_PLATFORM_WINDOWS
if ((project.mGeneralOptions.mTargetType == .BeefConsoleApplication) ||
(project.mGeneralOptions.mTargetType == .BeefGUIApplication))
2019-08-23 11:56:54 -07:00
{
if (workspaceOptions.mToolsetType != .GNU)
{
String pdbPath = scope String();
BuildContext.GetPdbPath(targetPath, workspaceOptions, options, pdbPath);
2019-08-23 11:56:54 -07:00
outPaths.Add(new String(pdbPath));
}
}
2024-08-27 05:48:22 +02:00
#endif
2019-08-23 11:56:54 -07:00
}
public class ArgBuilder
2019-08-23 11:56:54 -07:00
{
String mTarget;
bool mDoLongBreak;
int mLastBreak;
HashSet<String> mLinkPaths = new HashSet<String>() ~ DeleteContainerAndItems!(_);
public this(String target, bool doLongBreak)
{
mTarget = target;
mDoLongBreak = doLongBreak;
if (mDoLongBreak)
mLastBreak = mTarget.LastIndexOf('\n');
else
mLastBreak = 0;
}
public void AddSep()
{
if (mDoLongBreak)
{
if (mTarget.Length - mLastBreak > 0x1F000)
{
mLastBreak = mTarget.Length;
mTarget.Append('\n');
return;
}
}
mTarget.Append(' ');
}
2024-08-27 05:48:22 +02:00
2019-08-23 11:56:54 -07:00
public void AddFileName(String filePath)
{
IDEUtils.AppendWithOptionalQuotes(mTarget, filePath);
/*int lastSlash = Math.Max(filePath.LastIndexOf('\\'), filePath.LastIndexOf('/'));
if (lastSlash == -1)
{
IDEUtils.AppendWithOptionalQuotes(mTarget, filePath);
return;
}
String fileDir = scope String(filePath, 0, lastSlash);
String fileName = scope String(filePath, lastSlash + 1);
String* strPtr = null;
if (mLinkPaths.TryAdd(fileDir, out strPtr))
{
*strPtr = new String(fileDir);
mTarget.Append("-libpath:");
IDEUtils.AppendWithOptionalQuotes(mTarget, fileDir);
AddSep();
}
IDEUtils.AppendWithOptionalQuotes(mTarget, fileName);*/
}
}
2024-08-27 05:48:22 +02:00
Project FindProject(String projectName)
{
return mWorkspace.FindProject(projectName);
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public bool GetDependentProjectList(Project project, List<Project> orderedProjectList, List<Project> projectStack = null)
{
2019-08-23 11:56:54 -07:00
var useProjectStack = projectStack;
2024-08-27 05:48:22 +02:00
if ((useProjectStack != null) && (useProjectStack.Contains(project)))
{
String projectError = scope String("Circular dependency between projects: ");
for (int32 i = 0; i < useProjectStack.Count; i++)
{
if (i > 0)
projectError.Append(", ");
projectError.Append(useProjectStack[i].mProjectName);
}
OutputErrorLine(projectError);
return true;
}
if (orderedProjectList.Contains(project))
return true;
bool addSelf = true;
if (useProjectStack == null)
{
useProjectStack = scope:: List<Project>();
addSelf = false;
}
useProjectStack.Add(project);
for (var dep in project.mDependencies)
{
Project depProject = FindProject(dep.mProjectName);
if (depProject == null)
{
2024-12-31 14:15:12 -08:00
OutputLine(scope String()..AppendF("Unable to find project '{0}', a dependency of project '{1}'", dep.mProjectName, project.mProjectName));
2024-08-27 05:48:22 +02:00
return false;
}
if (!GetDependentProjectList(depProject, orderedProjectList, useProjectStack))
return false;
}
if (addSelf)
{
useProjectStack.RemoveAt(useProjectStack.Count - 1);
orderedProjectList.Add(project);
}
return true;
}
void GetOrderedProjectList(List<Project> orderedProjectList)
{
List<Project> projectStack = scope List<Project>();
for (var project in mWorkspace.mProjects)
GetDependentProjectList(project, orderedProjectList, projectStack);
}
2019-08-23 11:56:54 -07:00
public virtual void LoadFailed()
{
if (mRunningTestScript)
mFailed = true;
}
public virtual void TestFailed()
{
mLastTestFailed = true;
}
protected virtual void CompileFailed(Stopwatch stopwatch)
2024-08-27 05:48:22 +02:00
{
2019-08-23 11:56:54 -07:00
if (mTestManager != null)
mTestManager.BuildFailed();
if (mVerbosity > .Quiet)
OutputLineSmart("ERROR-SOFT: Compile failed. Total build time: {0:0.00}s", stopwatch.ElapsedMilliseconds / 1000.0f);
2019-08-23 11:56:54 -07:00
mLastCompileFailed = true;
if (mRunningTestScript)
{
if (!gApp.mScriptManager.mHadExpectingError)
gApp.mScriptManager.Fail("Compile failed");
}
else
{
Beep(MessageBeepType.Error);
}
2022-03-08 06:27:06 -08:00
if (mDebugger?.mIsComptimeDebug == true)
DebuggerComptimeStop();
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
void DbgCopyChangedFiles(DateTime cmpTime, StringView srcDir, StringView destDir)
{
bool isFirstFile = true;
for (let fileEntry in Directory.EnumerateFiles(srcDir))
{
var fileWriteTime = fileEntry.GetLastWriteTime();
String dates = scope .();
cmpTime.ToString(dates);
dates.Append("\n");
fileWriteTime.ToString(dates);
if (fileWriteTime > mDbgHighestTime)
mDbgHighestTime = fileWriteTime;
if (fileWriteTime < cmpTime)
{
continue;
}
String fileName = scope String();
fileEntry.GetFileName(fileName);
if (isFirstFile)
{
Directory.CreateDirectory(destDir).IgnoreError();
isFirstFile = false;
}
String srcFile = scope .();
srcFile.Append(srcDir);
srcFile.Append("/");
srcFile.Append(fileName);
String destFile = scope .();
destFile.Append(destDir);
destFile.Append("/");
destFile.Append(fileName);
File.Copy(srcFile, destFile);
}
for (let fileEntry in Directory.EnumerateDirectories(srcDir))
{
String dirName = scope String();
fileEntry.GetFileName(dirName);
String newSrcDir = scope String();
newSrcDir.Append(srcDir);
newSrcDir.Append("/");
newSrcDir.Append(dirName);
String newDestDir = scope String();
newDestDir.Append(destDir);
newDestDir.Append("/");
newDestDir.Append(dirName);
DbgCopyChangedFiles(cmpTime, newSrcDir, newDestDir);
}
}
void CompileResult(String hotProjectName, bool success)
{
if (mDbgCompileDir != null)
{
String compileResults = scope .();
compileResults.AppendF("COMPILE {} {} {}: ", mDbgCompileIdx, mConfigName, mPlatformName);
if (hotProjectName != null)
compileResults.Append("Hot compile. ");
if (!success)
compileResults.Append("FAILED\n");
else
compileResults.Append("SUCCESS\n");
String path = scope .();
path.Append(mDbgCompileDir);
path.Append("log.txt");
File.WriteAllText(path, compileResults, true);
var prevHighestTime = mDbgHighestTime;
for (var project in mWorkspace.mProjects)
{
String buildDir = scope .();
GetProjectBuildDir(project, buildDir);
String toPath = scope .();
toPath.Append(mDbgVersionedCompileDir);
toPath.Append(project.mProjectName);
toPath.Append("/");
DbgCopyChangedFiles(prevHighestTime, buildDir, toPath);
}
}
}
2024-08-27 05:48:22 +02:00
void ProcessBeefCompileResults(BfPassInstance passInstance, CompileKind compileKind, Project hotProject, Stopwatch startStopWatch)
{
2019-08-23 11:56:54 -07:00
bool didCompileSucceed = true;
if (passInstance != null)
{
if (mProfileCompileProfileId != 0)
mProfileCompileProfileId.Dispose();
2024-08-27 05:48:22 +02:00
while (true)
{
2019-08-23 11:56:54 -07:00
String str = scope String();
2024-08-27 05:48:22 +02:00
if (!passInstance.PopOutString(str))
2019-08-23 11:56:54 -07:00
break;
if (mVerbosity == .Quiet)
continue;
if (str.StartsWith(":"))
{
int spacePos = str.IndexOf(' ');
if (spacePos > 0)
{
bool wantsDisp = true;
StringView msgType = StringView(str, 0, spacePos);
if (msgType == ":warn")
{
mLastCompileHadMessages = true;
wantsDisp = mVerbosity >= .Minimal;
}
else if (msgType == ":error")
{
mLastCompileHadMessages = true;
}
else if (msgType == ":low")
wantsDisp = mVerbosity >= .Detailed;
else if (msgType == ":med")
wantsDisp = mVerbosity >= .Normal;
if (!wantsDisp)
continue;
str.Remove(0, spacePos + 1);
}
}
str.Append("\n");
OutputSmart(str);
2024-08-27 05:48:22 +02:00
//OutputLine(str);
}
2019-08-23 11:56:54 -07:00
if ((passInstance.mFailed) && (passInstance.mCompileSucceeded))
{
// This can happen if we can't load a Beef file
CompileFailed(startStopWatch);
2019-08-23 11:56:54 -07:00
passInstance.mCompileSucceeded = false;
}
2024-08-27 05:48:22 +02:00
didCompileSucceed = passInstance.mCompileSucceeded;
2019-08-23 11:56:54 -07:00
if (didCompileSucceed)
{
mLastCompileSucceeded = true;
mWorkspace.WithProjectItems(scope (item) =>
{
if (var projectSource = item as ProjectSource)
{
2024-08-27 05:48:22 +02:00
if (IsBeefFile(projectSource.mPath))
{
2019-08-23 11:56:54 -07:00
if (!projectSource.mHasChangedSinceLastCompile)
projectSource.mHasChangedSinceLastSuccessfulCompile = false;
}
}
});
}
else
mLastCompileHadMessages = true;
2024-08-27 05:48:22 +02:00
delete passInstance;
2019-08-23 11:56:54 -07:00
}
if ((hotProject != null) && (passInstance != null) && (didCompileSucceed))
{
if (!mDebugger.mIsRunning)
{
2020-04-30 10:34:37 -07:00
OutputErrorLine("Hot compile failed - target no longer running");
CompileFailed(startStopWatch);
2019-08-23 11:56:54 -07:00
return;
}
DebugManager.HotResolveFlags hotResolveFlags = .ActiveMethods;
if (mBfBuildCompiler.GetHasHotPendingDataChanges())
{
hotResolveFlags |= .Allocations;
mHotResolveState = .PendingWithDataChanges;
}
else
{
mHotResolveState = .Pending;
}
2024-08-27 05:48:22 +02:00
2019-08-23 11:56:54 -07:00
mHotResolveTryIdx = 0;
2024-08-27 05:48:22 +02:00
2019-08-23 11:56:54 -07:00
mDebugger.InitiateHotResolve(hotResolveFlags);
return;
}
List<Project> orderedProjectList = scope List<Project>();
2024-08-27 05:48:22 +02:00
GetOrderedProjectList(orderedProjectList);
if (!didCompileSucceed)
{
2019-08-23 11:56:54 -07:00
// Failed, bail out
for (var project in orderedProjectList)
{
2024-08-27 05:48:22 +02:00
SkipProjectCompile(project, hotProject);
2019-08-23 11:56:54 -07:00
}
CompileResult((hotProject != null) ? hotProject.mProjectName : null, false);
CompileFailed(startStopWatch);
2024-08-27 05:48:22 +02:00
return;
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
var completedCompileCmd = new BuildCompletedCmd();
2019-08-23 11:56:54 -07:00
if (hotProject != null)
completedCompileCmd.mHotProjectName = new String(hotProject.mProjectName);
//TODO: Pass in
//Project project = mWorkspace.mProjects[0];
2024-08-27 05:48:22 +02:00
bool success = true;
List<String> hotFileNames = scope List<String>();
2019-08-23 11:56:54 -07:00
defer ClearAndDeleteItems(hotFileNames);
Debug.Assert(mBuildContext == null);
DeleteAndNullify!(mBuildContext);
mBuildContext = new .();
mBuildContext.mWorkspaceOptions = GetCurWorkspaceOptions();
2024-08-27 05:48:22 +02:00
for (var project in orderedProjectList)
{
if (!mBuildContext.QueueProjectCompile(project, hotProject, completedCompileCmd, hotFileNames, compileKind))
success = false;
}
2019-08-23 11:56:54 -07:00
2021-02-25 10:14:22 -08:00
for (var project in orderedProjectList)
{
2024-08-27 05:48:22 +02:00
if (!mBuildContext.QueueProjectPostBuild(project, hotProject, completedCompileCmd, hotFileNames, compileKind))
success = false;
2021-02-25 10:14:22 -08:00
}
2024-08-27 05:48:22 +02:00
if (hotFileNames.Count > 0)
{
2019-08-23 11:56:54 -07:00
// Why were we rehupping BEFORE hotLoad?
2024-08-27 05:48:22 +02:00
mDebugger.RehupBreakpoints(false, false);
2019-08-23 11:56:54 -07:00
String[] entries = scope String[hotFileNames.Count];
for (int32 i = 0; i < hotFileNames.Count; i++)
entries[i] = hotFileNames[i];
//
mBfBuildCompiler.HotCommit();
2024-08-27 05:48:22 +02:00
mDebugger.HotLoad(entries, mWorkspace.HotCompileIdx);
/*if (mDebugger.IsPaused())
PCChanged();*/
2019-08-23 11:56:54 -07:00
//mDebugger.RehupBreakpoints(false);
RefreshWatches();
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if (!success)
CompileFailed(startStopWatch);
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if (success)
{
2020-03-23 12:07:05 -07:00
var options = GetCurWorkspaceOptions();
if (compileKind == .DebugAfter)
{
2024-08-27 05:48:22 +02:00
var startDebugCmd = new StartDebugCmd();
startDebugCmd.mWasCompiled = true;
2024-08-27 05:48:22 +02:00
startDebugCmd.mOnlyIfNotFailed = true;
startDebugCmd.mHotCompileEnabled = options.mAllowHotSwapping;
2024-08-27 05:48:22 +02:00
mExecutionQueue.Add(startDebugCmd);
}
else if (compileKind == .RunAfter)
{
StartupProject(false, true);
}
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
if (startStopWatch != null)
2024-08-27 05:48:22 +02:00
{
2019-08-23 11:56:54 -07:00
delete completedCompileCmd.mStopwatch;
var stopwatch = new Stopwatch();
stopwatch.CopyFrom(startStopWatch);
2024-08-27 05:48:22 +02:00
completedCompileCmd.mStopwatch = stopwatch;
2019-08-23 11:56:54 -07:00
}
2024-08-27 05:48:22 +02:00
mExecutionQueue.Add(completedCompileCmd);
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
bool CompileAndRun(bool debug)
{
2019-08-23 11:56:54 -07:00
if (AreTestsRunning())
return false;
if (IsCompiling)
return false;
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if (!mExecutionQueue.IsEmpty)
{
if (var processCompileCmd = mExecutionQueue.Back as ProcessBfCompileCmd)
{
processCompileCmd.mCompileKind = debug ? .DebugAfter : .RunAfter;
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
return false;
}
2019-08-23 11:56:54 -07:00
if (mInitialized)
DeleteAndNullify!(mLaunchData);
2024-08-27 05:48:22 +02:00
mOutputPanel.Clear();
OutputLine("Compiling...");
if (!Compile(debug ? .DebugAfter : .RunAfter, null))
2019-08-23 11:56:54 -07:00
return false;
return true;
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
public void QueueProfiling(int threadId, String desc, int sampleRate)
{
2024-08-27 05:48:22 +02:00
if (mExecutionQueue.IsEmpty)
2024-08-15 10:29:28 +00:00
return;
2019-08-23 11:56:54 -07:00
var profileCmd = new ProfileCmd();
profileCmd.mThreadId = threadId;
profileCmd.mDesc = new String(desc);
profileCmd.mSampleRate = sampleRate;
if (var processCompileCmd = mExecutionQueue.Back as ProcessBfCompileCmd)
{
delete processCompileCmd.mProfileCmd;
2019-08-23 11:56:54 -07:00
processCompileCmd.mProfileCmd = profileCmd;
}
}
[IDECommand]
void StopRunning()
{
if (AreTestsRunning())
{
mTestManager.Stop();
return;
}
bool hasTempFiles = false;
WithTabs(scope [&] (tabButton) =>
{
if (var svTabButton = tabButton as SourceViewTabButton)
{
if (svTabButton.mIsTemp)
hasTempFiles = true;
}
});
if (hasTempFiles)
{
2024-08-27 05:48:22 +02:00
var dialog = ThemeFactory.mDefault.CreateDialog("Close Temp Files",
"Do you want to close temporary files opened from the debugger?");
dialog.mDefaultButton = dialog.AddButton("Yes", new (evt) =>
{
List<SourceViewTabButton> closeTabs = scope .();
WithTabs(scope [&] (tabButton) =>
{
if (var svTabButton = tabButton as SourceViewTabButton)
{
if (svTabButton.mIsTemp)
closeTabs.Add(svTabButton);
}
});
for (var tab in closeTabs)
{
CloseDocument(tab.mContent);
2024-08-27 05:48:22 +02:00
}
});
dialog.AddButton("No", new (evt) =>
{
});
dialog.PopupWindow(GetActiveWindow());
}
2019-08-23 11:56:54 -07:00
if (mCrashDumpPath != null)
{
DeleteAndNullify!(mCrashDumpPath);
mDebugger.Detach();
mDebugger.mIsRunning = false;
mExecutionPaused = false;
}
if (mDebugger.mIsRunning)
{
2022-03-08 06:27:06 -08:00
if (mDebugger.mIsComptimeDebug)
CancelBuild();
2019-08-23 11:56:54 -07:00
mDebugger.StopDebugging();
}
}
public void AutoGenerateStartupCode(Project project)
{
2019-11-30 13:28:40 -08:00
// We have to save this to ensure any new project is actually created. Maybe overkill,
// but best to stay conservative since this path won't be heavily tested
if (!SaveAll())
return;
String namespaceName = scope .();
String className = scope .();
String startupStr = project.mBeefGlobalOptions.mStartupObject;
int dotPos = startupStr.LastIndexOf('.');
if (dotPos != -1)
{
namespaceName.Append(startupStr, 0, dotPos);
className.Append(startupStr, dotPos + 1);
}
else
{
namespaceName.Append(project.mProjectName);
className.Append(startupStr);
}
String startupCode = scope .();
startupCode.AppendF(
"""
using System;
2025-03-19 11:01:28 -04:00
2022-08-23 11:26:34 -07:00
namespace {};
2025-03-19 11:01:28 -04:00
2022-08-23 11:26:34 -07:00
class {}
{{
2022-08-23 11:26:34 -07:00
public static int Main(String[] args)
{{
2022-08-23 11:26:34 -07:00
return 0;
}}
}}
""", namespaceName, className);
String srcPath = scope .();
project.mRootFolder.GetFullImportPath(srcPath);
2019-11-30 13:28:40 -08:00
Directory.CreateDirectory(srcPath).IgnoreError();
srcPath.Append(Path.DirectorySeparatorChar);
srcPath.Append("Program.bf");
if (!SafeWriteTextFile(srcPath, startupCode))
return;
OnWatchedFileChanged(project.mRootFolder, .FileCreated, srcPath);
2020-01-12 09:21:50 -08:00
if (project.IsEmpty)
return;
let projectSource = project.mRootFolder.mChildItems[0] as ProjectSource;
if (projectSource == null)
return;
#if !CLI
ShowSourceFile(srcPath);
#endif
}
2021-02-25 10:14:22 -08:00
public bool IsVisualStudioRequired
{
get
{
var workspaceOptions = GetCurWorkspaceOptions();
2022-01-28 08:19:11 -05:00
if (Workspace.PlatformType.GetFromName(mPlatformName, workspaceOptions.mTargetTriple) != .Windows)
return false;
2021-02-25 10:14:22 -08:00
if (workspaceOptions.mToolsetType != .LLVM)
return true;
for (var project in mWorkspace.mProjects)
{
if ((project.mGeneralOptions.mTargetType != .BeefConsoleApplication) &&
(project.mGeneralOptions.mTargetType != .BeefGUIApplication) &&
(project.mGeneralOptions.mTargetType != .BeefApplication_DynamicLib) &&
(project.mGeneralOptions.mTargetType != .BeefApplication_StaticLib))
{
continue;
}
var options = GetCurProjectOptions(project);
if (options == null)
continue;
if (options.mBuildOptions.mCLibType != .SystemMSVCRT)
return true;
}
return false;
}
}
2024-08-27 05:48:22 +02:00
protected bool Compile(CompileKind compileKind, Project hotProject)
{
Debug.Assert(mBuildContext == null);
if (mWorkspace.mStartupProject != null)
{
if ((mWorkspace.mStartupProject.IsEmpty) && (!mWorkspace.IsDebugSession))
{
2020-01-12 09:21:50 -08:00
#if !CLI
DarkDialog dlg = new DarkDialog("Initialize Project?",
2020-01-21 09:06:37 -08:00
scope String()..AppendF("Project '{}' does not contain any source code. Do you want to auto-generate some startup code?", mWorkspace.mStartupProject.mProjectName)
, DarkTheme.sDarkTheme.mIconError);
dlg.mWindowFlags |= .Modal;
dlg.AddYesNoButtons(new (dlg) =>
{
AutoGenerateStartupCode(mWorkspace.mStartupProject);
},
new (dlg) =>
{
});
dlg.PopupWindow(GetActiveWindow());
2020-01-12 09:21:50 -08:00
#else
OutputErrorLine("The project '{}' does not contain any source code. Run with '-generate' to auto-generate some startup code.", mWorkspace.mStartupProject.mProjectName);
#endif
OutputLine("Aborted - no startup project code found.");
return false;
}
}
if ((compileKind != .Test) && (mWorkspace.mStartupProject != null) && (mWorkspace.mStartupProject.mGeneralOptions.mTargetType == .BeefTest))
2020-09-27 22:20:26 -07:00
{
OutputErrorLine("Test project '{}' has been selected as the Startup Project. Use the 'Test' menu to run or debug tests.", mWorkspace.mStartupProject.mProjectName);
return false;
}
2022-01-28 08:19:11 -05:00
var workspaceOptions = GetCurWorkspaceOptions();
var platform = Workspace.PlatformType.GetFromName(mPlatformName, workspaceOptions.mTargetTriple);
2019-10-01 12:46:38 -07:00
let hostPlatform = Workspace.PlatformType.GetHostPlatform();
if (platform == .Unknown)
{
OutputErrorLine("Failed to compiler for unknown platform '{}'", mPlatformName);
return false;
}
bool canCompile = false;
if (platform == hostPlatform)
{
canCompile = true;
}
else
{
canCompile = false;
}
2020-06-23 07:33:43 -07:00
canCompile = platform == hostPlatform;
switch (platform)
{
case .iOS:
canCompile = hostPlatform == .macOS;
case .Android:
canCompile = true;
case .Unknown:
canCompile = true;
2020-06-23 07:33:43 -07:00
case .Linux:
if (hostPlatform == .Windows)
canCompile = true; // Use WSL
2020-08-06 09:24:37 -07:00
case .Wasm:
canCompile = true;
default:
}
2024-08-27 05:48:22 +02:00
2019-10-01 12:46:38 -07:00
if (!canCompile)
{
OutputErrorLine("Cannot compile for platform '{}' from host platform '{}'", platform, hostPlatform);
return false;
}
2019-08-23 11:56:54 -07:00
if (mDbgCompileDump)
{
mDbgCompileIdx++;
String dbgBuildDir = scope .();
dbgBuildDir.Append(mInstallDir);
dbgBuildDir.Append("_dbg/");
String exePath = scope .();
Environment.GetExecutableFilePath(exePath);
Path.GetFileNameWithoutExtension(exePath, dbgBuildDir);
/*String exePath = scope .();
Environment.GetExecutableFilePath(exePath);
String exeName = scope .();
Path.GetFileNameWithoutExtension(exePath, exeName);
String dbgBuildDir = scope String(mInstallDir, "dbg_build_", exeName);*/
if (mDbgCompileIdx == 0)
{
String tempDirName = scope .();
tempDirName.Append(mInstallDir);
tempDirName.AppendF("_dbg/_{0}", DateTime.Now.Ticks);
if (Directory.Move(dbgBuildDir, tempDirName) case .Err(let err))
{
if (err != .NotFound)
{
2020-05-11 10:16:24 -07:00
Fail(scope String()..AppendF("Failed to rename {0} to {1}", dbgBuildDir, tempDirName));
2019-08-23 11:56:54 -07:00
return false;
}
}
String delDirName = new String(tempDirName);
ThreadPool.QueueUserWorkItem(new () =>
{
Utils.DelTree(delDirName).IgnoreError();
delete delDirName;
});
2024-08-27 05:48:22 +02:00
2019-08-23 11:56:54 -07:00
bool worked = false;
for (int i < 20)
{
if (Directory.CreateDirectory(dbgBuildDir) case .Ok)
{
worked = true;
break;
}
Thread.Sleep(100);
}
Debug.Assert(worked);
}
dbgBuildDir.Append("/");
if (mDbgCompileIdx == 0)
{
delete mDbgCompileDir;
mDbgCompileDir = new String(dbgBuildDir);
2024-08-27 05:48:22 +02:00
2019-08-23 11:56:54 -07:00
for (var project in mWorkspace.mProjects)
{
String buildDir = scope .();
GetProjectBuildDir(project, buildDir);
String toPath = scope .();
toPath.Append(mDbgCompileDir);
toPath.Append("/init/");
toPath.Append(project.mProjectName);
toPath.Append("/");
DbgCopyChangedFiles(default, buildDir, toPath);
}
}
dbgBuildDir.AppendF("{0}", mDbgCompileIdx);
Directory.CreateDirectory(dbgBuildDir);
DeleteAndNullify!(mDbgVersionedCompileDir);
mDbgVersionedCompileDir = new String(dbgBuildDir);
mDbgVersionedCompileDir.Append("/");
}
if ((AreTestsRunning()) && (compileKind != .Test))
{
return false;
}
if ((compileKind == .RunAfter) || (compileKind == .DebugAfter))
2019-08-23 11:56:54 -07:00
{
2024-10-25 07:41:53 -04:00
if ((workspaceOptions.mBuildKind == .Test) && (platform != .Wasm))
2019-08-23 11:56:54 -07:00
{
2020-09-27 22:20:26 -07:00
OutputErrorLine("Cannot directly run Test workspace configurations. Use the 'Test' menu to run or debug tests.");
2019-08-23 11:56:54 -07:00
return false;
}
}
2021-02-25 10:14:22 -08:00
if ((Workspace.PlatformType.GetFromName(mPlatformName) == .Windows) && (IsVisualStudioRequired))
2019-08-23 11:56:54 -07:00
{
if (!mSettings.mVSSettings.IsConfigured())
mSettings.mVSSettings.SetDefaults();
if (!mSettings.mVSSettings.IsConfigured())
{
2024-08-27 05:48:22 +02:00
String err =
"""
Beef requires the Microsoft C++ build tools for Visual Studio 2013 or later, but they don't seem to be installed.
2025-03-19 11:01:28 -04:00
2024-08-27 05:48:22 +02:00
Install just Microsoft Visual C++ Build Tools or the entire Visual Studio suite from:
https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2022
""";
2019-08-23 11:56:54 -07:00
#if CLI
Fail(err);
#else
DarkDialog dlg = new DarkDialog("Visual C++ Not Found", err, DarkTheme.sDarkTheme.mIconError);
dlg.mWindowFlags |= .Modal;
dlg.AddOkCancelButtons(new (dlg) =>
{
ProcessStartInfo psi = scope ProcessStartInfo();
2021-12-22 05:53:49 -05:00
psi.SetFileName("https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2022");
2019-08-23 11:56:54 -07:00
psi.UseShellExecute = true;
psi.SetVerb("Open");
var process = scope SpawnedProcess();
process.Start(psi).IgnoreError();
},
new (dlg) =>
{
});
((DarkButton)dlg.mButtons[0]).Label = "Open Link";
dlg.PopupWindow(GetActiveWindow());
2024-09-09 13:51:22 +02:00
Beep(.Error);
2019-08-23 11:56:54 -07:00
#endif
}
}
if (mProfileCompile)
mProfileCompileProfileId = Profiler.StartSampling("Compile");
if (mWantsBeefClean)
{
// We must finish cleaning before we can compile
while (mWantsBeefClean)
{
UpdateCompilersAndDebugger();
}
}
2024-08-27 05:48:22 +02:00
2019-08-23 11:56:54 -07:00
if ((!workspaceOptions.mIncrementalBuild) && (mCompileSinceCleanCount > 0) && (hotProject == null))
{
delete mBfBuildCompiler;
delete mBfBuildSystem;
mBfBuildSystem = new BfSystem();
mBfBuildCompiler = mBfBuildSystem.CreateCompiler(false);
for (var project in mWorkspace.mProjects)
{
2024-08-27 05:48:22 +02:00
mBfBuildSystem.AddProject(project);
2019-08-23 11:56:54 -07:00
}
}
if (compileKind == .DebugComptime)
{
mTargetDidInitBreak = true;
mTargetStartWithStep = false;
mDebugger.ComptimeAttach(mBfBuildCompiler);
mDebugger.RehupBreakpoints(true);
mBfBuildCompiler.ForceRebuild();
}
2019-08-23 11:56:54 -07:00
bool lastCompileHadMessages = mLastCompileHadMessages;
mLastCompileFailed = false;
mLastCompileSucceeded = false;
mLastCompileHadMessages = false;
mCompileSinceCleanCount++;
2024-08-27 05:48:22 +02:00
ShowPanel(mOutputPanel, false);
2019-08-23 11:56:54 -07:00
if (mDisableBuilding)
{
OutputErrorLine("Compiling disabled!");
2019-08-23 11:56:54 -07:00
return false;
}
2024-08-27 05:48:22 +02:00
if (mExecutionQueue.Count > 0)
return false;
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if ((mDebugger != null) && (mDebugger.mIsRunning) && (hotProject == null) && (compileKind != .WhileRunning))
2019-08-23 11:56:54 -07:00
{
Debug.Assert(!mDebugger.mIsRunningCompiled);
Debug.Assert((compileKind == .Normal) || (compileKind == .DebugComptime));
2019-08-23 11:56:54 -07:00
}
2024-08-27 05:48:22 +02:00
mHaveSourcesChangedExternallySinceLastCompile = false;
mHaveSourcesChangedInternallySinceLastCompile = false;
mWorkspace.SetupProjectCompileInstance(hotProject != null);
2019-08-23 11:56:54 -07:00
int32 hotIdx = mWorkspace.HotCompileIdx;
if (hotProject == null)
{
Debug.Assert(hotIdx == 0);
}
// Set position for breakpoints here only on first one.
// For hotload we do it later.
//if (!mDebugger.mIsRunning)
//mDebugger.RehupBreakpoints(true);
2024-08-27 05:48:22 +02:00
SaveClangFiles();
if ((compileKind == .RunAfter) || (compileKind == .DebugAfter))
{
2019-08-23 11:56:54 -07:00
DeleteAndNullify!(mCompileAndRunStopwatch);
2024-08-27 05:48:22 +02:00
mCompileAndRunStopwatch = new Stopwatch();
mCompileAndRunStopwatch.Start();
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
//mBfBuildCompiler.QueueStartTiming();
mBfBuildCompiler.ClearCompletionPercentage();
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
BfPassInstance passInstance = null;
2019-08-23 11:56:54 -07:00
passInstance = CompileBeef(hotProject, hotIdx, lastCompileHadMessages, let hadBeef);
2024-08-27 05:48:22 +02:00
if (passInstance == null)
{
CompileFailed(scope .());
2024-08-27 05:48:22 +02:00
return false;
}
ProcessBfCompileCmd processCompileCmd = new ProcessBfCompileCmd();
processCompileCmd.mBfPassInstance = passInstance;
processCompileCmd.mCompileKind = compileKind;
processCompileCmd.mHotProject = hotProject;
processCompileCmd.mStopwatch = new Stopwatch();
processCompileCmd.mStopwatch.Start();
2019-08-23 11:56:54 -07:00
processCompileCmd.mHadBeef = hadBeef;
2024-08-27 05:48:22 +02:00
mExecutionQueue.Add(processCompileCmd);
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
return true;
}
2019-08-23 11:56:54 -07:00
void CheckDebugVisualizers()
{
scope AutoBeefPerf("CheckDebugVisualizers");
String dbgVis = scope String();
dbgVis.Append(mInstallDir, "BeefDbgVis.toml");
mWorkspace.WithProjectItems(scope (projectItem) =>
{
var projectSource = projectItem as ProjectSource;
if (projectSource == null)
return;
if ((projectSource.mName.StartsWith("BeefDbgVis")) &&
(projectSource.mName.EndsWith(".toml")))
{
dbgVis.Append("\n");
projectSource.GetFullImportPath(dbgVis);
}
});
mDebugger.LoadDebugVisualizers(dbgVis);
//mDebugger.LoadDebugVisualizers(scope String(mInstallDir, "BeefDbgVis.toml"));
}
2024-08-27 05:48:22 +02:00
bool StartupProject(bool doDebug, bool wasCompiled)
{
2019-08-23 11:56:54 -07:00
mProfilePanel.Clear();
2024-08-27 05:48:22 +02:00
if (mWorkspace.mStartupProject == null)
{
OutputErrorLine("No startup project started");
return false;
}
2019-08-23 11:56:54 -07:00
var project = mWorkspace.mStartupProject;
var workspaceOptions = GetCurWorkspaceOptions();
2024-08-27 05:48:22 +02:00
var options = GetCurProjectOptions(project);
if (options == null)
{
OutputErrorLine("Startup project '{0}' not enabled", mWorkspace.mStartupProject.mProjectName);
return false;
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
mDebugger.ClearInvalidBreakpoints();
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
mTargetDidInitBreak = false;
mTargetHadFirstBreak = false;
2019-08-23 11:56:54 -07:00
//options.mDebugOptions.mCommand
2024-08-27 05:48:22 +02:00
String launchPathRel = scope String();
ResolveConfigString(mPlatformName, workspaceOptions, project, options, options.mDebugOptions.mCommand, "debug command", launchPathRel);
String arguments = scope String();
ResolveConfigString(mPlatformName, workspaceOptions, project, options, "$(Arguments)", "debug command arguments", arguments);
String workingDirRel = scope String();
ResolveConfigString(mPlatformName, workspaceOptions, project, options, "$(WorkingDir)", "debug working directory", workingDirRel);
2019-08-23 11:56:54 -07:00
var workingDir = scope String();
Path.GetAbsolutePath(workingDirRel, project.mProjectDir, workingDir);
2021-02-25 10:14:22 -08:00
String launchPath = scope String();
Path.GetAbsolutePath(launchPathRel, workingDir, launchPath);
String targetPath = scope .();
ResolveConfigString(mPlatformName, workspaceOptions, project, options, "$(TargetPath)", "Target path", targetPath);
IDEUtils.FixFilePath(launchPath);
IDEUtils.FixFilePath(targetPath);
if (launchPath.IsEmpty)
{
OutputErrorLine("No debug target path was specified");
return false;
}
2019-08-23 11:56:54 -07:00
if (workingDir.IsEmpty)
Path.GetDirectoryPath(launchPath, workingDir).IgnoreError();
2019-08-23 11:56:54 -07:00
2024-10-23 05:23:37 -04:00
if (launchPath.EndsWith(".html"))
{
2024-10-24 06:27:24 -04:00
arguments.Set(scope $"\"{launchPath}\"");
2024-10-23 05:23:37 -04:00
launchPath.Set(scope $"{gApp.mInstallDir}/WasmLaunch.exe");
}
2019-08-23 11:56:54 -07:00
if (!Directory.Exists(workingDir))
{
OutputErrorLine(scope String()..AppendF("Unable to locate working directory '{0}'", workingDir));
return false;
}
var envVars = scope Dictionary<String, String>();
defer { for (var kv in envVars) { delete kv.key; delete kv.value; } }
Environment.GetEnvironmentVariables(envVars);
for (var envVar in options.mDebugOptions.mEnvironmentVars)
{
int eqPos = envVar.IndexOf('=', 1);
if (eqPos == -1)
{
OutputErrorLine("Invalid environment variable: {0}", envVar);
}
else
{
Environment.SetEnvironmentVariable(envVars, StringView(envVar, 0, eqPos), StringView(envVar, eqPos + 1));
}
}
var envBlock = scope List<char8>();
Environment.EncodeEnvironmentVariables(envVars, envBlock);
if (launchPath.IsWhiteSpace)
2019-08-23 11:56:54 -07:00
{
Fail(scope String()..AppendF("No debug command specified in '{}' properties", project.mProjectName));
return false;
}
if (!doDebug)
{
let runCmd = QueueRun(launchPath, arguments, workingDir, .None);
runCmd.mRunFlags |= .NoWait;
return true;
}
2024-07-23 07:56:23 +02:00
if (mSettings.mDebugConsoleKind == .Embedded)
{
ShowConsole();
2024-08-27 05:48:22 +02:00
#if BF_PLATFORM_WINDOWS
2024-07-23 07:56:23 +02:00
mConsolePanel.Attach();
2024-08-27 05:48:22 +02:00
#endif
2024-07-23 07:56:23 +02:00
}
if (mSettings.mDebugConsoleKind == .RedirectToImmediate)
{
ShowImmediatePanel();
}
DebugManager.OpenFileFlags openFileFlags = .None;
if ((mSettings.mDebugConsoleKind == .RedirectToImmediate) || (mSettings.mDebugConsoleKind == .RedirectToOutput))
openFileFlags |= .RedirectStdOutput | .RedirectStdError;
2024-08-27 05:48:22 +02:00
if (!mDebugger.OpenFile(launchPath, targetPath, arguments, workingDir, envBlock, wasCompiled, workspaceOptions.mAllowHotSwapping, openFileFlags))
{
#if BF_PLATFORM_WINDOWS
2024-07-23 07:56:23 +02:00
if (!mSettings.mAlwaysEnableConsole)
mConsolePanel.Detach();
2024-08-27 05:48:22 +02:00
#endif
2019-08-23 11:56:54 -07:00
DeleteAndNullify!(mCompileAndRunStopwatch);
2024-08-27 05:48:22 +02:00
return false;
}
2019-08-23 11:56:54 -07:00
CheckDebugVisualizers();
mDebugger.mIsRunning = true;
mDebugger.IncrementSessionIdx();
2019-08-23 11:56:54 -07:00
WithSourceViewPanels(scope (sourceView) =>
{
sourceView.RehupAlias();
});
2024-08-27 05:48:22 +02:00
mDebugger.RehupBreakpoints(true);
mDebugger.Run();
2019-08-23 11:56:54 -07:00
mModulePanel.ModulesChanged();
2024-08-27 05:48:22 +02:00
if (mCompileAndRunStopwatch != null)
{
mCompileAndRunStopwatch.Stop();
OutputLine("Compile-to-debug time: {0:0.00}s", mCompileAndRunStopwatch.ElapsedMilliseconds / 1000.0f);
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if ((mTargetStartWithStep) && (mMainBreakpoint == null))
{
2025-03-19 11:01:28 -04:00
// The idea is that we don't want to step into static initializers, so we
// temporarily break on _main and then we single step
2019-08-23 11:56:54 -07:00
//mMainBreakpoint = mDebugger.CreateSymbolBreakpoint("_ZN3Hey4Dude3Bro9TestClass4MainEv");
2024-08-27 05:48:22 +02:00
if ((project.mGeneralOptions.mTargetType == Project.TargetType.BeefConsoleApplication) ||
(project.mGeneralOptions.mTargetType == Project.TargetType.BeefGUIApplication))
mMainBreakpoint = mDebugger.CreateSymbolBreakpoint("-BeefStartProgram");
else
{
2024-08-27 05:48:22 +02:00
mMainBreakpoint = mDebugger.CreateSymbolBreakpoint("-main");
#if BF_PLATFORM_WINDOWS
//mMainBreakpoint2 = mDebugger.CreateSymbolBreakpoint("-WinMain");
#endif
}
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
return true;
}
2019-08-23 11:56:54 -07:00
public void Attach(Process process, DebugManager.AttachFlags attachFlags)
{
UpdateTitle(process.ProcessName);
OutputLine("Attaching to process id {0}: {1}", process.Id, process.ProcessName);
mExecutionPaused = false;
mTargetDidInitBreak = false;
mTargetHadFirstBreak = false;
if (mDebugger.mIsRunning)
{
Fail("Already debugging a target");
return;
}
if (!mDebugger.Attach(process, attachFlags))
{
Fail("Failed to attach to process");
return;
}
CheckDebugVisualizers();
mDebugger.mIsRunning = true;
mDebugger.IncrementSessionIdx();
2019-08-23 11:56:54 -07:00
mDebugger.RehupBreakpoints(true);
mDebugger.Run();
mIsAttachPendingSourceShow = true;
}
2024-08-27 05:48:22 +02:00
void ShowPanel(Widget panel, bool setFocus = true)
{
WithTabs(scope (tab) =>
{
if (tab.mContent == panel)
tab.Activate(setFocus);
});
}
2019-08-23 11:56:54 -07:00
void ShowStartupFile()
{
2019-09-12 09:48:38 -07:00
bool hasSourceShown = false;
WithSourceViewPanels(scope [&] (panel) =>
{
hasSourceShown = true;
});
if (hasSourceShown)
return;
if (mWorkspace.mStartupProject != null)
{
bool didShow = false;
mWorkspace.mStartupProject.WithProjectItems(scope [&] (item) =>
{
if (didShow)
return;
if ((item.mName.Equals("main.bf", .OrdinalIgnoreCase)) ||
(item.mName.Equals("program.bf", .OrdinalIgnoreCase)))
{
ShowProjectItem(item, false);
didShow = true;
}
});
}
}
2024-08-27 05:48:22 +02:00
public void CreateDefaultLayout(bool allowSavedLayout = true)
{
2019-08-23 11:56:54 -07:00
//TODO:
//mConfigName.Set("Dbg");
if ((allowSavedLayout) && (!mRunningTestScript) && (!mIsFirstRun) && (LoadDefaultLayoutData()))
2019-08-23 11:56:54 -07:00
{
return;
}
2024-08-27 05:48:22 +02:00
bool docOnlyView = false;
TabbedView projectTabbedView = CreateTabbedView();
SetupTab(projectTabbedView, "Workspace", 0, mProjectPanel, false);
projectTabbedView.SetRequestedSize(GS!(200), GS!(200));
2019-10-05 10:24:58 -07:00
projectTabbedView.mWidth = GS!(200);
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
//TabbedView propertiesView = CreateTabbedView();
//propertiesView.AddTab("Properties", 0, mPropertiesPanel, false);
//propertiesView.SetRequestedSize(250, 250);
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if (!docOnlyView)
{
mDockingFrame.AddDockedWidget(projectTabbedView, null, DockingFrame.WidgetAlign.Left);
EnsureDocumentArea();
//mDockingFrame.AddDockedWidget(propertiesView, null, DockingFrame.WidgetAlign.Right);
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
var outputTabbedView = CreateTabbedView();
mDockingFrame.AddDockedWidget(outputTabbedView, null, DockingFrame.WidgetAlign.Bottom);
2019-10-05 10:24:58 -07:00
outputTabbedView.SetRequestedSize(GS!(250), GS!(250));
2024-08-27 05:48:22 +02:00
SetupTab(outputTabbedView, "Output", GS!(150), mOutputPanel, false);
SetupTab(outputTabbedView, "Immediate", GS!(150), mImmediatePanel, false);
//outputTabbedView.AddTab("Find Results", 150, mFindResultsPanel, false);
var watchTabbedView = CreateTabbedView();
watchTabbedView.SetRequestedSize(GS!(250), GS!(250));
mDockingFrame.AddDockedWidget(watchTabbedView, outputTabbedView, DockingFrame.WidgetAlign.Left);
SetupTab(watchTabbedView, "Auto", 150, mAutoWatchPanel, false);
SetupTab(watchTabbedView, "Watch", 150, mWatchPanel, false);
SetupTab(watchTabbedView, "Memory", 150, mMemoryPanel, false);
SetupTab(outputTabbedView, "Call Stack", 150, mCallStackPanel, false);
SetupTab(outputTabbedView, "Threads", 150, mThreadPanel, false);
}
protected void CreateBfSystems()
{
2019-08-23 11:56:54 -07:00
scope AutoBeefPerf("IDEApp.CreateBfSystems");
#if !CLI
if (!mNoResolve)
{
2024-08-27 05:48:22 +02:00
mBfResolveSystem = new BfSystem();
mBfResolveCompiler = mBfResolveSystem.CreateCompiler(true);
mBfResolveHelper = new BfResolveHelper();
2019-08-23 11:56:54 -07:00
}
#if IDE_C_SUPPORT
2024-08-27 05:48:22 +02:00
mResolveClang = new ClangCompiler(true);
2019-08-23 11:56:54 -07:00
#endif
#endif
mCompileSinceCleanCount = 0;
2024-08-27 05:48:22 +02:00
mBfBuildSystem = new BfSystem();
mBfBuildCompiler = mBfBuildSystem.CreateCompiler(false);
2019-08-23 11:56:54 -07:00
#if IDE_C_SUPPORT
2024-08-27 05:48:22 +02:00
mDepClang = new ClangCompiler(false);
mDepClang.mPairedCompiler = mResolveClang;
mResolveClang.mPairedCompiler = mDepClang;
2019-08-23 11:56:54 -07:00
#endif
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
void UpdateTitle(StringView titleOverride = default)
{
String title = scope String();
if ((mWorkspace != null) && (mWorkspace.mName != null))
{
title.Append(mWorkspace.mName);
title.Append(" - ");
}
if (titleOverride.Ptr != null)
{
title.Append(titleOverride);
title.Append(" - ");
}
title.Append("Beef IDE");
2019-09-18 08:13:00 -07:00
String extraStr = scope .();
String exePath = scope .();
Environment.GetExecutableFilePath(exePath);
if (exePath.Contains(@"\host\"))
extraStr.Append("host");
String exeFileName = scope .();
Path.GetFileNameWithoutExtension(exePath, exeFileName);
int slashPos = exeFileName.IndexOf('_');
if (slashPos != -1)
{
if (!extraStr.IsEmpty)
extraStr.Append(" ");
extraStr.Append(exeFileName, slashPos + 1);
}
if (!extraStr.IsEmpty)
title.AppendF(" [{}]", extraStr);
2019-08-23 11:56:54 -07:00
mMainWindow.SetTitle(title);
}
public void CreateSpellChecker()
{
2022-03-18 18:06:14 -07:00
#if !CLI
2019-08-23 11:56:54 -07:00
mSpellChecker = new SpellChecker();
if (mSpellChecker.Init(scope String(mInstallDir, "en_US")) case .Err)
{
DeleteAndNullify!(mSpellChecker);
}
2022-03-18 18:06:14 -07:00
#endif
2019-08-23 11:56:54 -07:00
}
2020-01-24 11:51:31 -08:00
public FileVersionInfo GetVersionInfo(out DateTime exeTime)
{
2020-01-24 11:51:31 -08:00
exeTime = default;
if (mVersionInfo == null)
{
String exeFilePath = scope .();
Environment.GetExecutableFilePath(exeFilePath);
mVersionInfo = new .();
mVersionInfo.GetVersionInfo(exeFilePath).IgnoreError();
2020-04-30 14:04:45 -07:00
if (!String.IsNullOrEmpty(mVersionInfo.FileVersion))
2020-05-31 09:17:21 -07:00
Debug.Assert(mVersionInfo.FileVersion.StartsWith(cVersion));
2020-01-24 13:45:30 -08:00
#if BF_PLATFORM_WINDOWS
2020-01-24 11:51:31 -08:00
exeTime = File.GetLastWriteTime(exeFilePath).GetValueOrDefault();
2020-01-24 13:45:30 -08:00
#endif
}
return mVersionInfo;
}
2019-08-23 11:56:54 -07:00
#if !CLI
2024-08-27 05:48:22 +02:00
public override void Init()
{
2019-08-23 11:56:54 -07:00
scope AutoBeefPerf("IDEApp.Init");
2019-09-27 13:03:47 -07:00
//int zag = 123;
if (mVerbosity == .Default)
mVerbosity = .Detailed;
mStartedWithTestScript = mRunningTestScript;
2019-08-23 11:56:54 -07:00
mCommands.Init();
EnableGCCollect = mEnableGCCollect;
if (mConfigName.IsEmpty)
mConfigName.Set("Debug");
if (mPlatformName.IsEmpty)
mPlatformName.Set(sPlatform64Name ?? sPlatform32Name);
2019-08-23 11:56:54 -07:00
Directory.GetCurrentDirectory(mInitialCWD);
2019-09-18 08:13:00 -07:00
#if DEBUG
//mColorMatrix = Matrix4.Identity;
#endif
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
base.Init();
2019-08-23 11:56:54 -07:00
mSettings.Apply();
2024-10-21 09:18:07 -04:00
mGitManager.Init();
2019-08-23 11:56:54 -07:00
//Yoop();
/*for (int i = 0; i < 100*1024*1024; i++)
{
mTestString.Append("Hey, this is a test string!");
}*/
mAutoDirty = false;
2024-08-27 05:48:22 +02:00
WidgetWindow.sOnWindowClosed.Add(new => HandleWindowClosed);
2019-08-23 11:56:54 -07:00
//mProjectDir = BFApp.mApp.mInstallDir + "../../";
if (!mRunningTestScript)
{
// User setting can affect automated testing, so use default settings
2019-10-05 10:24:58 -07:00
if (!mIsFirstRun)
mSettings.Load();
2019-08-23 11:56:54 -07:00
mSettings.Apply();
2019-09-16 09:32:02 -07:00
mIsFirstRun = !mSettings.mLoadedSettings;
2019-09-23 14:55:26 -07:00
#if !CLI && BF_PLATFORM_WINDOWS
if (!mSettings.mTutorialsFinished.mRanDebug)
{
let exePath = scope String();
Environment.GetExecutableFilePath(exePath);
if (exePath.EndsWith("_d.exe", .OrdinalIgnoreCase))
{
if (Windows.MessageBoxA(default, "Are you sure you want to run the debug build of the Beef IDE? This is useful for debugging Beef issues but execution speed will be much slower.", "RUN DEBUG?",
Windows.MB_ICONQUESTION | Windows.MB_YESNO) != Windows.IDYES)
{
Stop();
return;
}
}
mSettings.mTutorialsFinished.mRanDebug = true;
}
#endif
2019-08-23 11:56:54 -07:00
}
2020-05-25 11:02:09 -07:00
Font.AddFontFailEntry("Segoe UI", scope String()..AppendF("{}fonts/NotoSans-Regular.ttf", mInstallDir));
2024-08-27 05:48:22 +02:00
DarkTheme aTheme = new DarkTheme();
2020-08-05 05:37:05 -07:00
mSettings.mUISettings.Apply(); // Apply again to set actual theme
2024-08-27 05:48:22 +02:00
aTheme.Init();
ThemeFactory.mDefault = aTheme;
2019-08-23 11:56:54 -07:00
mTinyCodeFont = new Font();
mCodeFont = new Font();
2024-07-19 10:31:33 +02:00
mTermFont = new Font();
2019-08-23 11:56:54 -07:00
//mCodeFont = Font.LoadFromFile(BFApp.sApp.mInstallDir + "fonts/SourceCodePro32.fnt");
//mCodeFont = Font.LoadFromFile(BFApp.sApp.mInstallDir + "fonts/SegoeUI24.fnt");
2024-08-27 05:48:22 +02:00
mMainFrame = new MainFrame();
mDockingFrame = mMainFrame.mDockingFrame;
mSquiggleImage = Image.LoadFromFile(scope String(mInstallDir, "images/Squiggle.png"));
mCircleImage = Image.LoadFromFile(scope String(mInstallDir, "images/Circle.png"));
2019-08-23 11:56:54 -07:00
RehupScale();
2024-08-27 05:48:22 +02:00
CreateBfSystems();
2019-08-23 11:56:54 -07:00
if (mWantsClean)
{
mBfBuildCompiler.ClearBuildCache();
mWantsClean = false;
}
2024-08-27 05:48:22 +02:00
mProjectPanel = new ProjectPanel();
2019-08-23 11:56:54 -07:00
mProjectPanel.mAutoDelete = false;
mClassViewPanel = new ClassViewPanel();
mClassViewPanel.mAutoDelete = false;
2024-08-27 05:48:22 +02:00
mOutputPanel = new OutputPanel(true);
2019-08-23 11:56:54 -07:00
mOutputPanel.mAutoDelete = false;
2024-08-27 05:48:22 +02:00
#if BF_PLATFORM_WINDOWS
2024-07-19 10:31:33 +02:00
mTerminalPanel = new TerminalPanel();
2024-07-23 07:56:23 +02:00
mTerminalPanel .Init();
2024-07-19 10:31:33 +02:00
mTerminalPanel.mAutoDelete = false;
mConsolePanel = new ConsolePanel();
2024-07-23 07:56:23 +02:00
mConsolePanel.Init();
2024-07-19 10:31:33 +02:00
mConsolePanel.mAutoDelete = false;
2024-08-27 05:48:22 +02:00
#endif
mImmediatePanel = new ImmediatePanel();
2019-08-23 11:56:54 -07:00
mImmediatePanel.mAutoDelete = false;
2024-08-27 05:48:22 +02:00
mFindResultsPanel = new FindResultsPanel();
2019-08-23 11:56:54 -07:00
mFindResultsPanel.mAutoDelete = false;
2024-08-27 05:48:22 +02:00
mAutoWatchPanel = new WatchPanel(true);
2019-08-23 11:56:54 -07:00
mAutoWatchPanel.mAutoDelete = false;
2024-08-27 05:48:22 +02:00
mWatchPanel = new WatchPanel(false);
2019-08-23 11:56:54 -07:00
mWatchPanel.mAutoDelete = false;
2024-08-27 05:48:22 +02:00
mMemoryPanel = new MemoryPanel();
2019-08-23 11:56:54 -07:00
mMemoryPanel.mAutoDelete = false;
2024-08-27 05:48:22 +02:00
mCallStackPanel = new CallStackPanel();
2019-08-23 11:56:54 -07:00
mCallStackPanel.mAutoDelete = false;
2024-08-27 05:48:22 +02:00
mBreakpointPanel = new BreakpointPanel();
2019-08-23 11:56:54 -07:00
mBreakpointPanel.mAutoDelete = false;
2020-07-18 06:50:28 -07:00
mDiagnosticsPanel = new DiagnosticsPanel();
mDiagnosticsPanel.mAutoDelete = false;
mErrorsPanel = new ErrorsPanel();
mErrorsPanel.mAutoDelete = false;
2019-08-23 11:56:54 -07:00
mModulePanel = new ModulePanel();
mModulePanel.mAutoDelete = false;
2024-08-27 05:48:22 +02:00
mThreadPanel = new ThreadPanel();
2019-08-23 11:56:54 -07:00
mThreadPanel.mAutoDelete = false;
mProfilePanel = new ProfilePanel();
mProfilePanel.mAutoDelete = false;
2024-08-27 05:48:22 +02:00
mPropertiesPanel = new PropertiesPanel();
2019-08-23 11:56:54 -07:00
mPropertiesPanel.mAutoDelete = false;
mAutoCompletePanel = new AutoCompletePanel();
mAutoCompletePanel.mAutoDelete = false;
2022-06-08 20:11:39 +02:00
mBookmarksPanel = new BookmarksPanel();
mBookmarksPanel.mAutoDelete = false;
2019-08-23 11:56:54 -07:00
2020-01-24 11:51:31 -08:00
GetVersionInfo(var exeDate);
let localExeDate = exeDate.ToLocalTime();
String exeDateStr = scope .();
localExeDate.ToShortDateString(exeDateStr);
exeDateStr.Append(" at ");
localExeDate.ToShortTimeString(exeDateStr);
OutputLine("IDE Started. Version {} built {}.", mVersionInfo.FileVersion, exeDateStr);
2019-08-23 11:56:54 -07:00
/*if (!mRunningTestScript)
2024-08-27 05:48:22 +02:00
{
2019-08-23 11:56:54 -07:00
LoadUserSettings(); // User setting can affect automated testing, so use default settings
mSettings.Load();
mSettings.Apply();
}*/
2024-08-27 05:48:22 +02:00
if (mWorkspace.mDir != null)
{
mWorkspace.mName = new String();
Path.GetFileName(mWorkspace.mDir, mWorkspace.mName);
CustomBuildProperties.Load();
2024-08-27 05:48:22 +02:00
LoadWorkspace(mVerb);
}
2019-08-23 11:56:54 -07:00
else if (mWorkspace.IsSingleFileWorkspace)
{
mWorkspace.mName = new String();
Path.GetFileNameWithoutExtension(mWorkspace.mCompositeFile.mFilePath, mWorkspace.mName);
CustomBuildProperties.Load();
LoadWorkspace(mVerb);
2019-08-23 11:56:54 -07:00
}
2019-09-16 09:32:02 -07:00
bool loadedWorkspaceUserData = false;
2024-08-27 05:48:22 +02:00
if ((!mRunningTestScript) && (LoadWorkspaceUserData()))
2019-09-16 09:32:02 -07:00
{
loadedWorkspaceUserData = true;
}
2019-08-23 11:56:54 -07:00
if (mSetConfigName != null)
{
mConfigName.Set(mSetConfigName);
DeleteAndNullify!(mSetConfigName);
}
if (mSetPlatformName != null)
{
mPlatformName.Set(mSetPlatformName);
DeleteAndNullify!(mSetPlatformName);
}
2024-08-27 05:48:22 +02:00
WorkspaceLoaded();
2019-08-23 11:56:54 -07:00
2019-09-16 09:32:02 -07:00
if ((mIsFirstRun) && (!loadedWorkspaceUserData))
{
GetWorkspaceRect(var workX, var workY, var workWidth, var workHeight);
int32 height = (int32)(workHeight * 0.85f);
int32 width = Math.Min(4 * height / 3, (int32)(workWidth * 0.85f));
2024-08-27 05:48:22 +02:00
mRequestedWindowRect = .(workX + (workWidth - width) / 2, workY + (workHeight - height) / 2, width, height);
2019-09-16 09:32:02 -07:00
}
if (!loadedWorkspaceUserData)
CreateDefaultLayout();
2019-08-23 11:56:54 -07:00
//
{
BFWindow.Flags flags = .Border | .ThickFrame | .Resizable | .SysMenu |
2024-08-27 05:48:22 +02:00
.Caption | .Minimize | .Maximize | .QuitOnClose | .Menu | .PopupPosition | .AcceptFiles;
if (mRunningTestScript)
flags |= .NoActivate;
if (mRequestedShowKind == .Maximized || mRequestedShowKind == .ShowMaximized)
2019-09-27 13:03:47 -07:00
flags |= .ShowMaximized;
2019-08-23 11:56:54 -07:00
scope AutoBeefPerf("IDEApp.Init:CreateMainWindow");
2024-08-27 05:48:22 +02:00
mMainWindow = new WidgetWindow(null, "Beef IDE", (int32)mRequestedWindowRect.mX,
(int32)mRequestedWindowRect.mY, (int32)mRequestedWindowRect.mWidth, (int32)mRequestedWindowRect.mHeight,
flags, mMainFrame);
2019-08-23 11:56:54 -07:00
}
2019-09-16 09:32:02 -07:00
if (mIsFirstRun)
{
// If this is our first time running, set up a scale based on DPI
int dpi = mMainWindow.GetDPI();
if (dpi >= 120)
{
2020-08-05 05:37:05 -07:00
mSettings.mUISettings.mScale = 100 * Math.Min(dpi / 96.0f, 4.0f);
2019-10-05 10:24:58 -07:00
mSettings.Apply();
2024-08-27 05:48:22 +02:00
}
2019-09-16 09:32:02 -07:00
}
2019-08-23 11:56:54 -07:00
UpdateTitle();
2024-08-27 05:48:22 +02:00
mMainWindow.SetMinimumSize(GS!(480), GS!(360));
mMainWindow.mIsMainWindow = true;
mMainWindow.mOnMouseUp.Add(new => MouseUp);
2024-08-27 05:48:22 +02:00
mMainWindow.mOnWindowKeyDown.Add(new => SysKeyDown);
2024-07-19 10:31:33 +02:00
mMainWindow.mOnWindowKeyUp.Add(new => SysKeyUp);
2024-08-27 05:48:22 +02:00
mMainWindow.mOnWindowCloseQuery.Add(new => AllowClose);
2022-01-07 12:51:57 -03:00
mMainWindow.mOnDragDropFile.Add(new => DragDropFile);
2024-08-27 05:48:22 +02:00
CreateMenu();
UpdateRecentDisplayedFilesMenuItems();
if (mRecentlyDisplayedFiles.Count > 0)
ShowRecentFile(0);
mProjectPanel.RebuildUI();
2019-08-23 11:56:54 -07:00
if (mProcessAttachId != 0)
{
Process debugProcess = scope Process();
switch (debugProcess.GetProcessById(mProcessAttachId))
{
case .Ok:
case .Err:
2024-12-31 14:15:12 -08:00
Fail(scope String()..AppendF("Unable to locate process id {0}", mProcessAttachId));
2019-08-23 11:56:54 -07:00
}
if (debugProcess.IsAttached)
{
DebugManager.AttachFlags attachFlags = .None;
String titleName = scope String();
UpdateTitle(titleName);
OutputLine("Attaching to process id {0}: {1}", mProcessAttachId, titleName);
#if BF_PLATFORM_WINDOWS
if (!mProcessAttachHandle.IsInvalid)
attachFlags |= .ShutdownOnExit;
#endif
2024-08-27 05:48:22 +02:00
2019-08-23 11:56:54 -07:00
Attach(debugProcess, attachFlags);
#if BF_PLATFORM_WINDOWS
if (!mProcessAttachHandle.IsInvalid)
{
Windows.SetEvent(mProcessAttachHandle);
mTargetDidInitBreak = true;
mTargetHadFirstBreak = true;
}
#endif
}
}
else if (mCrashDumpPath != null)
{
if (mDebugger.OpenMiniDump(mCrashDumpPath))
{
mDebugger.mIsRunning = true;
mDebugger.IncrementSessionIdx();
2019-08-23 11:56:54 -07:00
mExecutionPaused = false; // Make this false so we can detect a Pause immediately
mIsAttachPendingSourceShow = true;
}
else
{
2024-12-31 14:15:12 -08:00
Fail(scope String()..AppendF("Failed to load minidump '{0}'", mCrashDumpPath));
2019-08-23 11:56:54 -07:00
}
}
else if (mLaunchData != null)
{
if (mLaunchData.mTargetPath == null)
{
if (mLaunchData.mPaused)
RunWithStep();
else
CompileAndRun(true);
}
else
LaunchDialog.DoLaunch(null, mLaunchData.mTargetPath, mLaunchData.mArgs ?? "", mLaunchData.mWorkingDir ?? "", "", mLaunchData.mPaused, true);
2019-08-23 11:56:54 -07:00
}
mInitialized = true;
//Profiler.StopSampling();
//if (IsMinidump)
//ShowOutput();
bool needSave = false;
/*if (mWorkspace.mIsLegacyWorkspace)
{
mWorkspace.mIsLegacyWorkspace = false;
mWorkspace.mHasChanged = true;
needSave = true;
}*/
/*for (var project in mWorkspace.mProjects)
{
if (project.mIsLegacyProject)
{
int slashPos = project.mProjectPath.LastIndexOf('\\');
if (slashPos != -1)
{
project.mProjectPath.RemoveToEnd(slashPos + 1);
project.mProjectPath.Append("BeefProj.toml");
project.mHasChanged = true;
project.mIsLegacyProject = false;
needSave = true;
}
//"BeefProj.toml"
}
}*/
if (needSave)
SaveAll();
ShowPanel(mOutputPanel, false);
UpdateRecentFileMenuItems();
ShowStartupFile();
2020-09-18 05:30:21 -07:00
#if !CLI
mFileRecovery.CheckWorkspace();
#endif
2019-09-23 09:41:21 -07:00
if ((mIsFirstRun) && (!mWorkspace.IsInitialized))
2019-09-23 11:50:11 -07:00
ShowWelcome();
2023-06-04 13:41:44 +02:00
if ((mSettings.mUISettings.mShowStartupPanel) && (!mIsFirstRun) && (!mWorkspace.IsInitialized))
ShowStartup();
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
#endif
2019-09-23 11:50:11 -07:00
void ShowWelcome()
{
WelcomePanel welcomePanel = new .();
TabbedView tabbedView = GetDefaultDocumentTabbedView();
let tabButton = SetupTab(tabbedView, "Welcome", 0, welcomePanel, true);
tabButton.Activate();
}
2019-08-23 11:56:54 -07:00
2023-06-04 13:41:44 +02:00
void ShowStartup()
{
StartupPanel startupPanel = new .();
TabbedView tabbedView = GetDefaultDocumentTabbedView();
let tabButton = SetupTab(tabbedView, "Startup", 0, startupPanel, true);
tabButton.Activate();
}
public void CheckLoadConfig()
{
if (mBeefConfig.mLibsChanged)
LoadConfig();
}
2024-10-25 17:20:00 -04:00
protected void LoadConfig()
2019-08-23 11:56:54 -07:00
{
delete mBeefConfig;
mBeefConfig = new BeefConfig();
if (mWorkspace.IsSingleFileWorkspace)
{
var dir = scope String();
Path.GetDirectoryPath(mWorkspace.mCompositeFile.FilePath, dir);
mBeefConfig.QueuePaths(dir);
}
else
mBeefConfig.QueuePaths(mWorkspace.mDir);
mBeefConfig.QueuePaths(mInstallDir);
mBeefConfig.Load().IgnoreError();
}
void RehupScale()
{
if (mInitialized)
Font.ClearCache();
if (mCodeFont == null)
return;
float fontSize = DarkTheme.sScale * mSettings.mEditorSettings.mFontSize;
2024-08-27 05:48:22 +02:00
float tinyFontSize = fontSize * 8.0f / 9.0f;
2019-08-23 11:56:54 -07:00
String err = scope String();
void FontFail(StringView name)
{
if (!err.IsEmpty)
err.Append("\n");
err.AppendF("Failed to load font '{}'", name);
}
2019-08-23 11:56:54 -07:00
bool isFirstFont = true;
for (var fontName in mSettings.mEditorSettings.mFonts)
2024-08-27 05:48:22 +02:00
FontLoop:
2019-08-23 11:56:54 -07:00
{
2024-08-27 05:48:22 +02:00
bool isOptional;
if (isOptional = fontName.StartsWith("?"))
fontName = scope:FontLoop String(fontName, "?".Length);
if (isFirstFont)
{
mTinyCodeFont.Dispose(true);
isFirstFont = !mTinyCodeFont.Load(fontName, tinyFontSize);
mCodeFont.Dispose(true);
if (!mCodeFont.Load(fontName, fontSize))
FontFail(fontName);
}
else
{
mTinyCodeFont.AddAlternate(fontName, tinyFontSize).IgnoreError();
if ((mCodeFont.AddAlternate(fontName, fontSize) case .Err) && (!isOptional))
FontFail(fontName);
}
2019-08-23 11:56:54 -07:00
}
if (mCodeFont.GetHeight() == 0)
{
mTinyCodeFont.Load("fonts/SourceCodePro-Regular.ttf", tinyFontSize);
mCodeFont.Load("fonts/SourceCodePro-Regular.ttf", fontSize);
}
// Do we have an extended font?
if (mCodeFont.GetWidth('😊') == 0)
{
mCodeFont.AddAlternate("Segoe UI", fontSize);
mCodeFont.AddAlternate("Segoe UI Symbol", fontSize).IgnoreError();
2019-09-26 10:06:10 -07:00
mCodeFont.AddAlternate("Segoe UI Historic", fontSize).IgnoreError();
2019-09-25 07:51:06 -07:00
mCodeFont.AddAlternate("Segoe UI Emoji", fontSize).IgnoreError();
2019-08-23 11:56:54 -07:00
mTinyCodeFont.AddAlternate("Segoe UI", tinyFontSize);
mTinyCodeFont.AddAlternate("Segoe UI Symbol", tinyFontSize).IgnoreError();
2019-09-26 10:06:10 -07:00
mTinyCodeFont.AddAlternate("Segoe UI Historic", tinyFontSize).IgnoreError();
2019-09-25 07:51:06 -07:00
mTinyCodeFont.AddAlternate("Segoe UI Emoji", tinyFontSize).IgnoreError();
2019-08-23 11:56:54 -07:00
/*mCodeFont.AddAlternate(new String("fonts/segoeui.ttf"), fontSize);
mCodeFont.AddAlternate(new String("fonts/seguisym.ttf"), fontSize);
mCodeFont.AddAlternate(new String("fonts/seguihis.ttf"), fontSize);
mTinyCodeFont.AddAlternate(new String("fonts/segoeui.ttf"), tinyFontSize);
mTinyCodeFont.AddAlternate(new String("fonts/seguisym.ttf"), tinyFontSize);
mTinyCodeFont.AddAlternate(new String("fonts/seguihis.ttf"), tinyFontSize);*/
}
2024-07-19 10:31:33 +02:00
mTermFont.Load("Cascadia Mono Regular", fontSize);
if (!err.IsEmpty)
{
OutputErrorLine(err);
Fail(err);
}
2019-08-23 11:56:54 -07:00
//mTinyCodeFont.Load(scope String(BFApp.sApp.mInstallDir, "fonts/SourceCodePro-Regular.ttf"), fontSize);
float squiggleScale = DarkTheme.sScale;
if (squiggleScale < 3.0f)
squiggleScale = (float)Math.Round(squiggleScale);
mSquiggleImage.Scale(squiggleScale);
mCircleImage.Scale(DarkTheme.sScale);
2020-03-28 14:26:59 -07:00
mMainWindow?.SetMinimumSize(GS!(480), GS!(360));
/*for (var window in gApp.mWindows)
{
2025-03-19 11:01:28 -04:00
2020-03-28 14:26:59 -07:00
window.SetMinimumSize(GS!());
}*/
2019-08-23 11:56:54 -07:00
}
2024-08-27 05:48:22 +02:00
void HandleWindowClosed(BFWindow window)
{
if (mWindowDatas.GetAndRemove(window) case .Ok((?, var windowData)))
2019-08-23 11:56:54 -07:00
{
delete windowData;
}
2024-08-27 05:48:22 +02:00
}
WindowData GetWindowData(BFWindow window)
{
WindowData windowData;
mWindowDatas.TryGetValue(window, out windowData);
if (windowData == null)
{
windowData = new WindowData();
mWindowDatas[window] = windowData;
}
return windowData;
}
public DrawLayer GetOverlayLayer(BFWindow window)
{
WindowData windowData = GetWindowData(window);
if (windowData.mOverlayLayer == null)
windowData.mOverlayLayer = new DrawLayer(window);
return windowData.mOverlayLayer;
}
void UpdateDrawTracking()
{
}
2019-08-23 11:56:54 -07:00
public enum EvalResult
{
Normal,
Pending
}
2020-05-08 07:10:35 -07:00
public EvalResult DebugEvaluate(String expectedType, String expr, String outVal, int cursorPos = -1, DebugManager.Language language = .NotSet,
DebugManager.EvalExpressionFlags expressionFlags = .None, delegate void(String) pendingHandler = null)
2019-08-23 11:56:54 -07:00
{
defer
{
if (mPendingDebugExprHandler !== pendingHandler)
2019-08-23 11:56:54 -07:00
delete pendingHandler;
}
mIsImmediateDebugExprEval = false;
String evalStr = scope String();
evalStr.Append(expr);
if (expectedType != null)
{
evalStr.Append(", expectedType=");
evalStr.Append(expectedType);
}
if (mDebugger.IsPaused())
{
String curMethodOwner = scope String();
int32 stackLanguage = 0;
2024-08-27 05:48:22 +02:00
mDebugger.GetStackMethodOwner(mDebugger.mActiveCallStackIdx, curMethodOwner, out stackLanguage);
2019-08-23 11:56:54 -07:00
if ((curMethodOwner.Length > 0) && (stackLanguage == 2) && (mWorkspace.mStartupProject != null)) // Beef only
{
String namespaceSearch = scope String();
if (mBfResolveSystem != null)
{
mBfResolveSystem.Lock(2);
BfProject startupProject = mBfResolveSystem.GetBfProject(mWorkspace.mStartupProject);
mBfResolveSystem.GetNamespaceSearch(curMethodOwner, namespaceSearch, startupProject);
mBfResolveSystem.Unlock();
}
if (!namespaceSearch.IsEmpty)
{
evalStr.Append(", namespaceSearch=\"");
int namespaceIdx = 0;
for (var namespaceEntry in namespaceSearch.Split('\n'))
{
if (namespaceIdx > 0)
evalStr.Append(",");
evalStr.Append(namespaceEntry);
namespaceIdx++;
}
evalStr.Append("\"");
}
}
}
mDebugger.Evaluate(evalStr, outVal, cursorPos, (int)language, expressionFlags);
if (outVal == "!pending")
{
Debug.Assert(mPendingDebugExprHandler == null);
mPendingDebugExprHandler = pendingHandler;
return .Pending;
}
return .Normal;
}
2022-03-08 06:27:06 -08:00
void DebuggerComptimeStop()
{
mDebugger.DisposeNativeBreakpoints();
mDebugger.Detach();
}
2024-08-27 05:48:22 +02:00
void DebuggerPaused()
{
mDebugger.IncrementStateIdx();
2024-08-27 05:48:22 +02:00
mDebugger.mActiveCallStackIdx = 0;
mExecutionPaused = true;
2019-08-23 11:56:54 -07:00
mDebugger.GetRunState();
WithWatchPanels(scope (watchPanel) =>
{
watchPanel.SetDisabled(false);
});
2024-08-27 05:48:22 +02:00
mMemoryPanel.SetDisabled(false);
mCallStackPanel.SetDisabled(false);
}
2019-08-23 11:56:54 -07:00
void WithWatchPanels(delegate void(WatchPanel watchPanel) dlg)
{
if (mWatchPanel == null)
return;
2019-08-23 11:56:54 -07:00
dlg(mWatchPanel);
dlg(mAutoWatchPanel);
for (let window in mWindows)
{
if (let widgetWindow = window as WidgetWindow)
{
if (let quickWatch = widgetWindow.mRootWidget as QuickWatchDialog)
{
dlg(quickWatch.[Friend]mWatchPanel);
}
}
}
}
2024-08-27 05:48:22 +02:00
void DebuggerUnpaused()
{
2019-08-23 11:56:54 -07:00
// Leave target in background for a bit, incase we immediately
// hit another breakpoint
2024-08-27 05:48:22 +02:00
mDebugger.mActiveCallStackIdx = 0;
mForegroundTargetCountdown = 30;
mExecutionPaused = false;
2019-08-23 11:56:54 -07:00
WithWatchPanels(scope (watchPanel) =>
{
watchPanel.SetDisabled(true);
});
2024-08-27 05:48:22 +02:00
mMemoryPanel.SetDisabled(true);
mCallStackPanel.SetDisabled(true);
2019-08-23 11:56:54 -07:00
// If hoverwatch is up, we need to close it so we refresh the value
2024-08-27 05:48:22 +02:00
var sourceViewPanel = GetActiveSourceViewPanel();
if ((sourceViewPanel != null) && (sourceViewPanel.mHoverWatch != null))
{
sourceViewPanel.mHoverWatch.Close();
}
2019-08-23 11:56:54 -07:00
mDebuggerContinueIdx++;
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public void StepIntoSpecific(int addr)
{
if (mExecutionPaused)
{
DebuggerUnpaused();
mDebugger.StepIntoSpecific(addr);
}
}
2019-08-23 11:56:54 -07:00
void FinishPendingHotResolve()
{
List<uint8> typeData = scope .();
String stackListStr = scope .();
if (!mDebugger.GetHotResolveData(typeData, stackListStr))
return;
2024-08-27 05:48:22 +02:00
2019-08-23 11:56:54 -07:00
mBfBuildCompiler.HotResolve_Start((mHotResolveState == .Pending) ? .None : .HadDataChanges);
mHotResolveState = .None;
for (int typeId < typeData.Count)
{
if (typeData[typeId] != 0)
mBfBuildCompiler.HotResolve_ReportType(typeId, .Heap);
}
String stackStr = scope .();
for (let stackStrView in stackListStr.Split('\n'))
{
stackStr.Set(stackStrView);
if (stackStr.StartsWith("D "))
{
stackStr.Remove(0, 2);
mBfBuildCompiler.HotResolve_AddDelegateMethod(stackStr);
}
else
mBfBuildCompiler.HotResolve_AddActiveMethod(stackStr);
}
String hotResult = scope String();
mBfBuildCompiler.HotResolve_Finish(hotResult);
if (hotResult.IsEmpty)
{
//OutputLineSmart("Hot type data changes have been resolved");
ProcessBeefCompileResults(null, .Normal, mWorkspace.mStartupProject, null);
2019-08-23 11:56:54 -07:00
}
else
{
if (mHotResolveTryIdx == 0)
{
2020-09-26 06:03:29 -07:00
OutputErrorLine("Hot type data changes cannot be applied because of the following types");
2019-08-23 11:56:54 -07:00
for (var line in hotResult.Split('\n'))
{
OutputLineSmart(" {0}", line);
}
OutputLineSmart("Revert changes or put the program into a state where the type is not actively being used");
var compileInstance = mWorkspace.mCompileInstanceList.Back;
Debug.Assert(compileInstance.mCompileResult == .PendingHotLoad);
compileInstance.mCompileResult = .Failure;
}
}
if (mHotResolveState == .None)
{
var compileInstance = mWorkspace.mCompileInstanceList.Back;
if (compileInstance.mCompileResult == .PendingHotLoad)
compileInstance.mCompileResult = .Success;
}
}
void QueueProjectItems(Project project)
{
project.WithProjectItems(scope (projectItem) =>
{
2024-08-27 05:48:22 +02:00
var projectSource = projectItem as ProjectSource;
if (projectSource != null)
{
2019-08-23 11:56:54 -07:00
if (projectSource.mIncludeKind == .Ignore)
return;
2024-08-27 05:48:22 +02:00
var resolveCompiler = GetProjectCompilerForFile(projectSource.mPath);
if (resolveCompiler == mBfResolveCompiler)
resolveCompiler.QueueProjectSource(projectSource, .None, false);
2019-08-23 11:56:54 -07:00
projectSource.mHasChangedSinceLastCompile = true;
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
});
}
void RemoveProjectItems(Project project)
{
project.WithProjectItems(scope (projectItem) =>
{
2024-08-27 05:48:22 +02:00
var projectSource = projectItem as ProjectSource;
if (projectSource != null)
{
var resolveCompiler = GetProjectCompilerForFile(projectSource.mPath);
if ((resolveCompiler == mBfResolveCompiler) && (resolveCompiler != null))
resolveCompiler.QueueProjectSourceRemoved(projectSource);
2019-08-23 11:56:54 -07:00
if (IsBeefFile(projectSource.mPath))
{
mBfBuildCompiler.QueueProjectSourceRemoved(projectSource);
}
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
});
}
2024-08-27 05:48:22 +02:00
void UpdateCompilersAndDebugger()
{
2019-08-23 11:56:54 -07:00
scope AutoBeefPerf("UpdateCompilersAndDebugger");
var msg = scope String();
2024-08-27 05:48:22 +02:00
while (true)
{
DebugManager.RunState runState = .NotStarted;
2019-08-23 11:56:54 -07:00
if (mDebugger != null)
2024-08-27 05:48:22 +02:00
{
mDebugger.Update();
2019-08-23 11:56:54 -07:00
2024-07-23 07:56:23 +02:00
Platform.BfpFile* stdOut = null;
Platform.BfpFile* stdError = null;
mDebugger.GetStdHandles(null, &stdOut, &stdError);
if (stdOut != null)
{
FileStream fileStream = new FileStream();
fileStream.Attach(stdOut);
Thread thread = new Thread(new => ReadDebugOutputThread);
thread.Start(fileStream, true);
}
if (stdError != null)
{
FileStream fileStream = new FileStream();
fileStream.Attach(stdError);
Thread thread = new Thread(new => ReadDebugOutputThread);
thread.Start(fileStream, true);
}
using (mDebugOutputMonitor.Enter())
{
if (!mDebugOutput.IsEmpty)
{
mDebugOutput.Replace("\r", "");
if (mSettings.mDebugConsoleKind == .RedirectToOutput)
mOutputPanel.Write(mDebugOutput);
if (mSettings.mDebugConsoleKind == .RedirectToImmediate)
mImmediatePanel.Write(mDebugOutput);
mDebugOutput.Clear();
}
}
2019-08-23 11:56:54 -07:00
runState = mDebugger.GetRunState();
mDebuggerPerformingTask = (runState == .DebugEval) || (runState == .DebugEval_Done) || (runState == .SearchingSymSrv);
2024-08-27 05:48:22 +02:00
if (mDebugAutoRun)
{
if (runState == DebugManager.RunState.NotStarted)
{
2019-08-23 11:56:54 -07:00
mOutputPanel.Clear();
2024-08-27 05:48:22 +02:00
if ((mExecutionQueue.Count == 0) && (!mDebugger.mIsRunning))
{
mTargetStartWithStep = true;
var startDbgCmd = new StartDebugCmd();
2019-08-23 11:56:54 -07:00
startDbgCmd.mWasCompiled = true;
2024-08-27 05:48:22 +02:00
startDbgCmd.mOnlyIfNotFailed = true;
mExecutionQueue.Add(startDbgCmd);
}
}
else if (runState == DebugManager.RunState.Paused)
{
2019-08-23 11:56:54 -07:00
mDebugger.Continue();
2024-08-27 05:48:22 +02:00
//mDebugger.Terminate();
}
}
2019-08-23 11:56:54 -07:00
if ((mPendingDebugExprHandler != null) && (runState != .DebugEval) && (runState != .SearchingSymSrv))
{
var evalResult = scope String();
mDebugger.EvaluateContinue(evalResult);
if (evalResult != "!pending")
{
mPendingDebugExprHandler(evalResult);
DeleteAndNullify!(mPendingDebugExprHandler);
}
}
if (mHotResolveState != .None)
{
FinishPendingHotResolve();
}
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if (mDebugAutoBuild)
{
if ((!mBfBuildCompiler.HasQueuedCommands()) && (mExecutionInstances.Count == 0) && (mExecutionQueue.Count == 0))
{
OutputLine("Autobuilding...");
CompileBeef(null, 0, mLastCompileSucceeded, let hadBeef);
}
}
2019-08-23 11:56:54 -07:00
bool didClean = false;
2024-08-27 05:48:22 +02:00
if (mWantsBeefClean)
{
2020-12-23 08:53:38 -08:00
mBfBuildCompiler?.CancelBackground();
mBfResolveCompiler?.CancelBackground();
2024-08-27 05:48:22 +02:00
if ((!mBfBuildCompiler.HasQueuedCommands()) &&
2019-08-23 11:56:54 -07:00
((mBfResolveCompiler == null) || (!mBfResolveCompiler.HasQueuedCommands())))
2024-08-27 05:48:22 +02:00
{
2019-08-23 11:56:54 -07:00
didClean = true;
2024-08-27 05:48:22 +02:00
OutputLine("Cleaned Beef.");
2019-08-23 11:56:54 -07:00
if (mErrorsPanel != null)
mErrorsPanel.ClearParserErrors(null);
2024-08-27 05:48:22 +02:00
DeleteAndNullify!(mBfResolveCompiler);
DeleteAndNullify!(mBfResolveSystem);
DeleteAndNullify!(mBfResolveHelper);
2024-08-27 05:48:22 +02:00
DeleteAndNullify!(mBfBuildCompiler);
DeleteAndNullify!(mBfBuildSystem);
2019-08-23 11:56:54 -07:00
///
2024-08-27 05:48:22 +02:00
mDebugger.FullReportMemory();
2019-08-23 11:56:54 -07:00
var workspaceBuildDir = scope String();
GetWorkspaceBuildDir(workspaceBuildDir);
if (Directory.Exists(workspaceBuildDir))
{
var result = Utils.DelTree(workspaceBuildDir, scope (fileName) =>
2024-08-27 05:48:22 +02:00
{
2019-08-23 11:56:54 -07:00
return fileName.EndsWith("build.dat");
2024-08-27 05:48:22 +02:00
});
if (result case .Err)
2019-08-23 11:56:54 -07:00
{
var str = scope String("Failed to delete folder: ", workspaceBuildDir);
Fail(str);
}
}
//ResolveConfigString(project, options, "$(TargetPath)", error, targetPath);
if (!mNoResolve)
{
2024-08-27 05:48:22 +02:00
mBfResolveSystem = new BfSystem();
mBfResolveCompiler = mBfResolveSystem.CreateCompiler(true);
mBfResolveHelper = new BfResolveHelper();
2019-08-23 11:56:54 -07:00
}
mCompileSinceCleanCount = 0;
2024-08-27 05:48:22 +02:00
mBfBuildSystem = new BfSystem();
mBfBuildCompiler = mBfBuildSystem.CreateCompiler(false);
2019-08-23 11:56:54 -07:00
mBfBuildCompiler.ClearBuildCache();
/*foreach (var project in mWorkspace.mProjects)
{
2024-08-27 05:48:22 +02:00
mBfBuildSystem.AddProject(project);
2019-08-23 11:56:54 -07:00
if (mBfResolveSystem != null)
2024-08-27 05:48:22 +02:00
mBfResolveSystem.AddProject(project);
}
2025-03-19 11:01:28 -04:00
2024-08-27 05:48:22 +02:00
foreach (var project in mWorkspace.mProjects)
{
project.WithProjectItems(scope (projectItem) =>
{
var projectSource = projectItem as ProjectSource;
if (projectSource != null)
2025-03-19 11:01:28 -04:00
{
2024-08-27 05:48:22 +02:00
var resolveCompiler = GetProjectCompilerForFile(projectSource.mPath);
if (resolveCompiler == mBfResolveCompiler)
resolveCompiler.QueueProjectSource(projectSource);
}
});
2019-08-23 11:56:54 -07:00
}
2024-08-27 05:48:22 +02:00
mBfResolveCompiler.QueueDeferredResolveAll();
CurrentWorkspaceConfigChanged();*/
mWantsBeefClean = false;
}
}
if (mWantsClean)
{
2020-12-23 08:53:38 -08:00
mBfBuildCompiler?.CancelBackground();
mBfResolveCompiler?.CancelBackground();
2024-08-27 05:48:22 +02:00
if ((!mBfBuildCompiler.HasQueuedCommands()) && (!mBfResolveCompiler.HasQueuedCommands())
#if IDE_C_SUPPORT
&& (!mDepClang.HasQueuedCommands()) && (!mResolveClang.HasQueuedCommands())
2019-08-23 11:56:54 -07:00
#endif
2024-08-27 05:48:22 +02:00
)
{
OutputLine("Cleaned.");
2019-08-23 11:56:54 -07:00
if (mErrorsPanel != null)
mErrorsPanel.ClearParserErrors(null);
2019-08-23 11:56:54 -07:00
let workspaceOptions = GetCurWorkspaceOptions();
delete mBfResolveHelper;
2024-08-27 05:48:22 +02:00
delete mBfResolveCompiler;
delete mBfResolveSystem;
2019-08-23 11:56:54 -07:00
#if IDE_C_SUPPORT
2024-08-27 05:48:22 +02:00
delete mResolveClang;
2019-08-23 11:56:54 -07:00
#endif
2024-08-27 05:48:22 +02:00
delete mBfBuildCompiler;
delete mBfBuildSystem;
2019-08-23 11:56:54 -07:00
#if IDE_C_SUPPORT
2024-08-27 05:48:22 +02:00
delete mDepClang;
2019-08-23 11:56:54 -07:00
#endif
//
2024-08-27 05:48:22 +02:00
CreateBfSystems();
2019-08-23 11:56:54 -07:00
mBfBuildCompiler.ClearBuildCache();
2024-08-27 05:48:22 +02:00
var workspaceBuildDir = scope String();
GetWorkspaceBuildDir(workspaceBuildDir);
if (Directory.Exists(workspaceBuildDir))
{
2019-08-23 11:56:54 -07:00
var result = Utils.DelTree(workspaceBuildDir);
2024-08-27 05:48:22 +02:00
if (result case .Err)
2019-08-23 11:56:54 -07:00
{
var str = scope String("Failed to delete folder: ", workspaceBuildDir);
//result.Exception.ToString(str);
2024-08-27 05:48:22 +02:00
Fail(str);
2019-08-23 11:56:54 -07:00
//result.Dispose();
}
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
List<String> deleteFails = scope .();
for (var project in mWorkspace.mProjects)
{
2020-03-28 14:26:59 -07:00
let options = GetCurProjectOptions(project);
if (options == null)
continue;
if (!options.mBuildOptions.mCleanCmds.IsEmpty)
{
if (mBuildContext == null)
mBuildContext = new BuildContext();
mBuildContext.QueueProjectCustomBuildCommands(project, "", .Always, options.mBuildOptions.mCleanCmds);
}
// Force running the "if files changed" commands
project.mForceCustomCommands = true;
project.mNeedsTargetRebuild = true;
2019-08-23 11:56:54 -07:00
// Don't delete custom build artifacts
if (project.mGeneralOptions.mTargetType == .CustomBuild)
continue;
List<String> projectFiles = scope .();
defer ClearAndDeleteItems(projectFiles);
GetTargetPaths(project, mPlatformName, workspaceOptions, options, projectFiles);
2019-08-23 11:56:54 -07:00
for (let filePath in projectFiles)
{
if (File.Delete(filePath) case .Err(let errVal))
{
if (File.Exists(filePath))
deleteFails.Add(new String(filePath));
}
}
}
if (!deleteFails.IsEmpty)
{
String str = scope String();
if (deleteFails.Count == 1)
str.AppendF("Failed to delete file: {0}", deleteFails[0]);
else
{
str.Append("Failed to delete files:");
for (let fileName in deleteFails)
{
str.Append("\n ", fileName);
}
}
Fail(str);
}
ClearAndDeleteItems(deleteFails);
didClean = true;
2024-08-27 05:48:22 +02:00
mWantsClean = false;
2019-08-23 11:56:54 -07:00
mDbgCompileIdx = -1;
mDbgHighestTime = default;
2024-08-27 05:48:22 +02:00
}
}
2019-08-23 11:56:54 -07:00
if (didClean)
{
for (var project in mWorkspace.mProjects)
{
2024-08-27 05:48:22 +02:00
mBfBuildSystem.AddProject(project);
2019-08-23 11:56:54 -07:00
if (mBfResolveSystem != null)
2024-08-27 05:48:22 +02:00
mBfResolveSystem.AddProject(project);
2019-08-23 11:56:54 -07:00
}
if (mBfResolveSystem != null)
{
PreConfigureBeefSystem(mBfResolveSystem, mBfResolveCompiler);
for (var project in mWorkspace.mProjects)
{
SetupBeefProjectSettings(mBfResolveSystem, mBfResolveCompiler, project);
}
}
CurrentWorkspaceConfigChanged();
2019-08-23 11:56:54 -07:00
if (mBfResolveSystem != null)
{
/*for (var project in mWorkspace.mProjects)
2019-08-23 11:56:54 -07:00
{
if (IsProjectEnabled(project))
QueueProjectItems(project);
}*/
2019-08-23 11:56:54 -07:00
mBfResolveCompiler.QueueDeferredResolveAll();
}
}
2024-08-27 05:48:22 +02:00
mBfBuildSystem.Update();
mBfBuildCompiler.Update();
while (true)
{
2019-08-23 11:56:54 -07:00
#if CLI
if (mCompilingBeef)
break;
2024-08-27 05:48:22 +02:00
#endif
2019-08-23 11:56:54 -07:00
msg.Clear();
2024-08-27 05:48:22 +02:00
if (!mBfBuildCompiler.PopMessage(msg))
2019-08-23 11:56:54 -07:00
break;
2024-08-27 05:48:22 +02:00
OutputLineSmart(msg);
}
2019-08-23 11:56:54 -07:00
if (mBfResolveSystem != null)
{
2024-08-27 05:48:22 +02:00
mBfResolveSystem.Update();
mBfResolveCompiler.Update();
mBfResolveCompiler.ClearMessages();
mBfResolveHelper.Update();
2019-08-23 11:56:54 -07:00
}
#if IDE_C_SUPPORT
2024-08-27 05:48:22 +02:00
mDepClang.Update();
if ((!IsCompiling) && (!mDepClang.IsPerformingBackgroundOperation()))
{
2019-08-23 11:56:54 -07:00
// Only leave mCompileWaitsForQueueEmpty false for a fast initial build
2024-08-27 05:48:22 +02:00
mDepClang.mCompileWaitsForQueueEmpty = true;
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
while (true)
{
2019-08-23 11:56:54 -07:00
msg.Clear();
2024-08-27 05:48:22 +02:00
if (!mDepClang.PopMessage(msg))
2019-08-23 11:56:54 -07:00
break;
2024-08-27 05:48:22 +02:00
OutputLine(msg);
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
mResolveClang.Update();
mResolveClang.ClearMessages();
2019-08-23 11:56:54 -07:00
#endif
2024-08-27 05:48:22 +02:00
bool wantDebuggerDetach = false;
if ((mDebugger != null) && (runState == DebugManager.RunState.Terminated))
{
wantDebuggerDetach = true;
}
2019-08-23 11:56:54 -07:00
String deferredMsgType = scope String();
String deferredOutput = scope String();
2024-08-27 05:48:22 +02:00
bool hadMessages = false;
while (mDebugger != null)
{
2019-08-23 11:56:54 -07:00
msg.Clear();
2024-08-27 05:48:22 +02:00
if (!mDebugger.PopMessage(msg))
2019-08-23 11:56:54 -07:00
{
break;
}
2024-08-27 05:48:22 +02:00
hadMessages = true;
int paramIdx = msg.IndexOf(' ');
StringView cmd = default;
StringView param = default;
2019-08-23 11:56:54 -07:00
if (paramIdx > 0)
{
cmd = msg.Substring(0, paramIdx);
param = msg.Substring(paramIdx + 1);
2019-08-23 11:56:54 -07:00
}
else
cmd = msg;
bool isOutput = (cmd == "msg") || (cmd == "dbgEvalMsg") || (cmd == "log");
if (cmd == "msgLo")
{
2019-09-04 10:27:37 -07:00
if (mVerbosity < .Diagnostic)
continue;
isOutput = true;
}
2024-08-27 05:48:22 +02:00
if (isOutput)
{
2019-08-23 11:56:54 -07:00
if (deferredMsgType != cmd)
{
if (deferredOutput.Length > 0)
{
OutputFormatted(deferredOutput, deferredMsgType == "dbgEvalMsg");
deferredOutput.Clear();
}
deferredMsgType.Set(cmd);
}
2024-08-27 05:48:22 +02:00
2019-08-23 11:56:54 -07:00
deferredOutput.Append(param);
2024-08-27 05:48:22 +02:00
}
else if ((cmd == "error") || (cmd == "errorsoft"))
{
2020-07-19 11:57:04 -07:00
if ((mRunningTestScript) && (!IsCrashDump) && (!mScriptManager.IsErrorExpected(param, false)))
2019-08-23 11:56:54 -07:00
mScriptManager.Fail(param);
bool isFirstMsg = true;
String tempStr = scope String();
bool focusOutput = false;
while (true)
{
StringView errorMsg = default;
2019-08-23 11:56:54 -07:00
int infoPos = param.IndexOf("\x01");
if (infoPos == 0)
{
Widget addWidget = null;
LeakWidget leakWidget = null;
int endPos = param.IndexOf("\n");
if (endPos == -1)
break;
String leakStr = scope String(param, 1, endPos - 1);
param.RemoveFromStart(endPos + 1);
2019-08-23 11:56:54 -07:00
int itemIdx = 0;
for (var itemView in leakStr.Split('\t'))
{
var item = scope String(itemView);
if (itemIdx == 0)
{
if (item == "LEAK")
{
focusOutput = true;
leakWidget = new LeakWidget(mOutputPanel);
leakWidget.Resize(0, GS!(-2), DarkTheme.sUnitSize, DarkTheme.sUnitSize);
addWidget = leakWidget;
}
else if (item == "TEXT")
{
OutputLine(scope String(leakStr, @itemView.MatchPos + 1));
break;
}
else
break;
}
else if (itemIdx == 1)
{
leakWidget.mExpr = new String(item);
}
else
{
//leakWidget.mStackAddrs.Add()
}
itemIdx++;
}
if (addWidget != null)
mOutputPanel.AddInlineWidget(addWidget);
}
else if (infoPos != -1)
{
tempStr.Clear();
tempStr.Append(param, 0, infoPos);
errorMsg = tempStr;
param.RemoveFromStart(infoPos);
2019-08-23 11:56:54 -07:00
}
else
errorMsg = param;
if (!errorMsg.IsEmpty)
2019-08-23 11:56:54 -07:00
{
if (isFirstMsg)
{
if (mTestManager != null)
{
// Give test manager time to flush
Thread.Sleep(100);
mTestManager.Update();
mOutputPanel.Update();
}
OutputLineSmart(scope String("ERROR: ", scope String(errorMsg)));
2022-04-22 19:19:43 -07:00
if ((gApp.mRunningTestScript) || (cmd == "errorsoft"))
2019-09-04 05:57:56 -07:00
{
// The 'OutputLineSmart' would already call 'Fail' when running test scripts
}
else
{
Fail(scope String(errorMsg));
2019-09-04 05:57:56 -07:00
}
2019-08-23 11:56:54 -07:00
isFirstMsg = false;
}
else
Output(scope String(errorMsg));
2019-08-23 11:56:54 -07:00
}
if (infoPos == -1)
break;
}
if (focusOutput)
ShowOutput();
2024-08-27 05:48:22 +02:00
}
else if (cmd == "memoryBreak")
{
Breakpoint breakpoint = null;
int memoryAddress = (int)int64.Parse(param, System.Globalization.NumberStyles.HexNumber);
for (var checkBreakpoint in mDebugger.mBreakpointList)
{
if (checkBreakpoint.mMemoryAddress == memoryAddress)
breakpoint = checkBreakpoint;
}
2024-12-31 17:42:13 -08:00
String infoString = scope .();
2019-08-23 11:56:54 -07:00
if (breakpoint != null)
2024-12-31 17:42:13 -08:00
infoString.AppendF("Memory breakpoint hit: '0x{0:X08}' ({1})", (int64)memoryAddress, breakpoint.mMemoryWatchExpression);
else
infoString.AppendF("Memory breakpoint hit: '0x{0:X08}'", (int64)memoryAddress);
2019-08-23 11:56:54 -07:00
OutputLine(infoString);
if (!mRunningTestScript)
{
2024-08-27 05:48:22 +02:00
MessageDialog("Breakpoint Hit", infoString);
Beep(MessageBeepType.Information);
2019-08-23 11:56:54 -07:00
}
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
else if (cmd == "newProfiler")
{
2024-08-27 05:48:22 +02:00
var profiler = mDebugger.PopProfiler();
2019-08-23 11:56:54 -07:00
//ShowProfilePanel();
mProfilePanel.Add(profiler);
}
else if (cmd == "showProfiler")
{
ShowProfilePanel();
}
else if (cmd == "clearOutput")
{
mOutputPanel.Clear();
}
else if (cmd == "dbgInfoLoaded")
{
//Debug.WriteLine("dbgInfoLoaded {0}", param);
mWantsRehupCallstack = true;
}
else if (cmd == "rehupLoc")
{
mWantsRehupCallstack = true;
}
else if (cmd == "modulesChanged")
{
mModulePanel.ModulesChanged();
}
else if (cmd == "symsrv")
{
mSymSrvStatus.Set(param);
}
2024-08-25 09:30:49 -04:00
else if (cmd == "script")
{
String absTestPath = scope String();
if (mWorkspace.mDir != null)
Path.GetAbsolutePath(param, mWorkspace.mDir, absTestPath);
else
Path.GetFullPath(param, absTestPath);
if (mScriptManager.mFailed)
mScriptManager.Clear();
mScriptManager.mSoftFail = true;
2024-08-27 05:48:22 +02:00
mScriptManager.SetTimeoutMS(20 * 60 * 1000); // 20 minute timeout
2024-08-25 09:30:49 -04:00
mScriptManager.QueueCommandFile(absTestPath);
}
2024-08-27 05:48:22 +02:00
else
{
Runtime.FatalError("Invalid debugger message type");
}
}
2019-08-23 11:56:54 -07:00
if (deferredOutput.Length > 0)
{
2024-08-27 05:48:22 +02:00
OutputFormatted(deferredOutput, deferredMsgType == "dbgEvalMsg");
2019-08-23 11:56:54 -07:00
}
2025-03-19 11:01:28 -04:00
/*if (hadMessages)
mNoDebugMessagesTick = 0;
2019-08-23 11:56:54 -07:00
else if (IDEApp.sApp.mIsUpdateBatchStart)
mNoDebugMessagesTick++;
if (mNoDebugMessagesTick < 10)
wantDebuggerDetach = false; // Don't detach if we have messages*/
2024-08-27 05:48:22 +02:00
if (wantDebuggerDetach)
{
2019-08-23 11:56:54 -07:00
OutputLine("Detached debugger");
UpdateTitle();
2024-08-27 05:48:22 +02:00
var disassemblyPanel = TryGetDisassemblyPanel(false);
if (disassemblyPanel != null)
disassemblyPanel.Disable();
#if BF_PLATFORM_WINDOWS
2024-07-23 07:56:23 +02:00
if (!mSettings.mAlwaysEnableConsole)
mConsolePanel.Detach();
2024-08-27 05:48:22 +02:00
#endif
mDebugger.DisposeNativeBreakpoints();
mDebugger.Detach();
mDebugger.mIsRunning = false;
2019-08-23 11:56:54 -07:00
mExecutionPaused = false;
mHotResolveState = .None;
mDebuggerPerformingTask = false;
2024-08-27 05:48:22 +02:00
mWorkspace.StoppedRunning();
mBreakpointPanel.BreakpointsChanged();
2019-08-23 11:56:54 -07:00
mModulePanel.ModulesChanged();
if (mPendingDebugExprHandler != null)
{
mPendingDebugExprHandler(null);
DeleteAndNullify!(mPendingDebugExprHandler);
}
mMemoryPanel.SetDisabled(true);
WithWatchPanels(scope (watchPanel) =>
{
watchPanel.SetDisabled(true);
});
mIsAttachPendingSourceShow = false;
2024-08-27 05:48:22 +02:00
}
if ((mDebugger != null) && (mDebugger.IsPaused()) && (!mDebugger.HasPendingDebugLoads()))
{
mForegroundTargetCountdown = 0;
if (!mExecutionPaused)
{
if (!mTargetDidInitBreak)
{
mTargetDidInitBreak = true;
2019-08-23 11:56:54 -07:00
bool wantsContinue = true;
2024-08-27 05:48:22 +02:00
if (mTargetStartWithStep)
{
2019-08-23 11:56:54 -07:00
if ((mMainBreakpoint != null) && (!mMainBreakpoint.IsBound()))
{
BfLog.LogDbg("Deleting mMainBreakpoint 1\n");
// Couldn't bind to main breakpoint, bind to entry point address
// "." is the magic symbol for "entry point"
String[] tryNames = scope .("WinMain", ".");
for (let name in tryNames)
{
mDebugger.DeleteBreakpoint(mMainBreakpoint);
mMainBreakpoint = mDebugger.CreateSymbolBreakpoint("WinMain");
if ((name == ".") || (mMainBreakpoint.IsBound()))
break;
}
2019-08-23 11:56:54 -07:00
}
wantsContinue = true;
2024-08-27 05:48:22 +02:00
}
else
{
if (mMainBreakpoint != null)
{
2019-08-23 11:56:54 -07:00
if (mMainBreakpoint.IsActiveBreakpoint())
wantsContinue = true;
BfLog.LogDbg("Deleting mMainBreakpoint 2\n");
mDebugger.DeleteBreakpoint(mMainBreakpoint);
2024-08-27 05:48:22 +02:00
//mMainBreakpoint.Dispose();
mMainBreakpoint = null;
}
}
2019-08-23 11:56:54 -07:00
if (wantsContinue)
{
DebuggerUnpaused();
mDebugger.Continue();
}
2024-08-27 05:48:22 +02:00
}
else
{
if (mMainBreakpoint != null)
{
2019-08-23 11:56:54 -07:00
BfLog.LogDbg("Deleting mMainBreakpoint 3\n");
2024-08-27 05:48:22 +02:00
mDebugger.DeleteBreakpoint(mMainBreakpoint);
mMainBreakpoint = null;
int addr;
String fileName = null;
mDebugger.UpdateCallStack();
var stackInfo = scope String();
mDebugger.GetStackFrameInfo(0, out addr, fileName, stackInfo);
if (stackInfo.Contains("BeefStartProgram"))
{
2019-08-23 11:56:54 -07:00
// Okay, NOW we can do a "step into"
2024-08-27 05:48:22 +02:00
if (!IsInDisassemblyMode())
mDebugger.StepInto(false);
return;
}
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if ((!HasModalDialogs()) && (!mRunningTestScript))
mMainWindow.SetForeground();
2019-08-23 11:56:54 -07:00
if (mRunTimingProfileId != 0)
{
mRunTimingProfileId.Dispose();
mRunTimingProfileId = 0;
}
BeefPerf.Event("Debugger Paused", "");
2024-08-27 05:48:22 +02:00
DebuggerPaused();
PCChanged();
2019-08-23 11:56:54 -07:00
mBreakpointPanel.MarkStatsDirty();
2024-08-27 05:48:22 +02:00
mTargetHadFirstBreak = true;
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
switch (mDebugger.GetRunState())
{
case .Exception:
2024-08-27 05:48:22 +02:00
String exceptionLine = scope String();
mDebugger.GetCurrentException(exceptionLine);
var exceptionData = String.StackSplit!(exceptionLine, '\n');
2019-08-23 11:56:54 -07:00
2024-12-31 14:15:12 -08:00
String exHeader = scope String()..AppendF("Exception {0}", exceptionData[1]);
2024-08-27 05:48:22 +02:00
if (exceptionData.Count >= 3)
exHeader = exceptionData[2];
2019-08-23 11:56:54 -07:00
2024-12-31 14:15:12 -08:00
String exString = scope String()..AppendF("{0} at {1}", exHeader, exceptionData[0]);
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
OutputLine(exString);
2019-08-23 11:56:54 -07:00
if (!IsCrashDump)
2024-08-27 05:48:22 +02:00
Fail(exString);
default:
2024-08-27 05:48:22 +02:00
}
}
}
break;
}
else if ((mDebugger != null) && (mDebugger.GetRunState() == .TargetUnloaded))
{
if (mWorkspace.mHadHotCompileSinceLastFullCompile)
{
// Had hot compiles - we need to recompile!
Compile(.WhileRunning);
}
else
{
mDebugger.Continue();
}
mWorkspace.StoppedRunning();
break;
}
2024-08-27 05:48:22 +02:00
else if (mDebuggerPerformingTask)
2019-08-23 11:56:54 -07:00
{
break;
}
else
2024-08-27 05:48:22 +02:00
{
if (mForegroundTargetCountdown > 0)
{
if ((--mForegroundTargetCountdown == 0) && (mDebugger.mIsRunning))
{
2024-08-27 05:48:22 +02:00
#if BF_PLATFORM_WINDOWS
if (mConsolePanel.mBeefConAttachState case .Connected(let processId))
2024-08-27 05:48:22 +02:00
mDebugger.ForegroundTarget(processId);
else
mDebugger.ForegroundTarget(0);
2024-08-27 05:48:22 +02:00
#endif
}
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if ((mDebugger != null) && (mExecutionPaused) && (mDebugger.mIsRunning))
DebuggerUnpaused();
break;
}
}
2019-08-23 11:56:54 -07:00
if ((mDebugger != null) && ((mDebugger.IsPaused()) || (!mDebugger.mIsRunning)) && (!IsCompiling))
{
if (mDebugger.mRunToCursorBreakpoint != null)
{
BfLog.LogDbg("Stopped. Clearing mRunToCursorBreakpoint\n");
mDebugger.DeleteBreakpoint(mDebugger.mRunToCursorBreakpoint);
mDebugger.mRunToCursorBreakpoint = null;
}
}
if ((mDebugger != null) && (mExecutionPaused) && (mDebugger.IsPaused()))
{
if (mWantsRehupCallstack)
{
mDebugger.RehupCallstack();
if (var deferredShowPCRequest = mDeferredUserRequest as DeferredShowPCLocation)
{
//Debug.WriteLine("Had DeferredShowPCLocation");
var stackIdx = deferredShowPCRequest.mStackIdx;
IDEApp.sApp.mDebugger.mActiveCallStackIdx = stackIdx;
ClearDeferredUserRequest();
ShowPCLocation(stackIdx, false, true);
StackPositionChanged();
}
mCallStackPanel.MarkCallStackDirty();
mThreadPanel.MarkThreadsDirty();
mModulePanel.ModulesChanged();
mWantsRehupCallstack = false;
}
}
if (mBuildContext != null)
{
mBuildContext.mUpdateCnt++;
bool isCompiling = (!mExecutionInstances.IsEmpty) || (!mExecutionQueue.IsEmpty);
if (mBuildContext.mScriptManager != null)
{
mBuildContext.mScriptManager.Update();
if ((mBuildContext.mScriptManager.HasQueuedCommands) && (!mBuildContext.mScriptManager.mFailed))
isCompiling = true;
}
if (!isCompiling)
{
DeleteAndNullify!(mBuildContext);
}
}
if ((mBuildContext == null) && (!IsCompiling))
DeleteAndNullify!(mLaunchData);
2020-01-20 17:12:07 -08:00
mErrorsPanel?.UpdateAlways();
2024-08-27 05:48:22 +02:00
#if BF_PLATFORM_WINDOWS
if ((mConsolePanel != null) && (mConsolePanel.mBeefConAttachState case .Attached(let consoleProcessId)))
{
if (!mDebugger.mIsRunning)
{
mConsolePanel.Detach();
}
else
{
int32 debugProcessId = mDebugger.GetProcessId();
if (debugProcessId != 0)
{
BeefConConsoleProvider.Pipe pipe = scope .();
pipe.Connect(Process.CurrentId, -consoleProcessId).IgnoreError();
//pipe.Connect(Process.CurrentId, )
//pipe.Connect(123, -consoleProcessId).IgnoreError();
pipe.StartMessage(.Attached);
pipe.Stream.Write(debugProcessId);
pipe.EndMessage();
mConsolePanel.Detach();
mConsolePanel.mBeefConAttachState = .Connected(consoleProcessId);
mDebugger.ForegroundTarget(consoleProcessId);
}
}
}
2024-08-27 05:48:22 +02:00
#endif
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public void ShowPassOutput(BfPassInstance bfPassInstance)
{
while (true)
{
var outString = scope String();
if (!bfPassInstance.PopOutString(outString))
2019-08-23 11:56:54 -07:00
break;
2024-08-27 05:48:22 +02:00
OutputLine(outString);
}
}
/*public bool CheckMouseover(Widget checkWidget, int32 wantTicks, out Point mousePoint)
{
mousePoint = Point(Int32.MinValue, Int32.MinValue);
2025-03-19 11:01:28 -04:00
if (checkWidget != mLastMouseWidget)
2024-08-27 05:48:22 +02:00
return false;
checkWidget.RootToSelfTranslate(mLastRelMousePos.x, mLastRelMousePos.y, out mousePoint.x, out mousePoint.y);
return mMouseStillTicks == wantTicks;
}
2019-08-23 11:56:54 -07:00
void LastMouseWidgetDeleted(Widget widget)
{
if (mLastMouseWidget == widget)
mLastMouseWidget = null;
}
void SetLastMouseWidget(Widget newWidget)
{
if (mLastMouseWidget != null)
mLastMouseWidget.mDeletedHandler.Remove(scope => LastMouseWidgetDeleted, true);
mLastMouseWidget = newWidget;
if (mLastMouseWidget != null)
mLastMouseWidget.mDeletedHandler.Add(new => LastMouseWidgetDeleted);
}
2024-08-27 05:48:22 +02:00
void UpdateMouseover()
{
2019-08-23 11:56:54 -07:00
if (mMouseStillTicks != -1)
2024-08-27 05:48:22 +02:00
mMouseStillTicks++;
Widget overWidget = null;
int32 numOverWidgets = 0;
foreach (var window in mWindows)
{
var widgetWindow = window as WidgetWindow;
2025-03-19 11:01:28 -04:00
2024-08-27 05:48:22 +02:00
widgetWindow.RehupMouse(false);
var windowOverWidget = widgetWindow.mCaptureWidget ?? widgetWindow.mOverWidget;
if ((windowOverWidget != null) && (widgetWindow.mAlpha == 1.0f) && (widgetWindow.mCaptureWidget == null))
{
overWidget = windowOverWidget;
numOverWidgets++;
if (overWidget != mLastMouseWidget)
{
2025-03-19 11:01:28 -04:00
SetLastMouseWidget(overWidget);
2024-08-27 05:48:22 +02:00
mMouseStillTicks = -1;
}
2019-08-23 11:56:54 -07:00
float actualX = widgetWindow.mClientX + widgetWindow.mMouseX;
float actualY = widgetWindow.mClientY + widgetWindow.mMouseY;
2024-08-27 05:48:22 +02:00
if ((mLastAbsMousePos.x != actualX) || (mLastAbsMousePos.y != actualY))
{
mMouseStillTicks = 0;
mLastAbsMousePos.x = actualX;
mLastAbsMousePos.y = actualY;
}
2019-08-23 11:56:54 -07:00
mLastRelMousePos.x = widgetWindow.mMouseX;
mLastRelMousePos.y = widgetWindow.mMouseY;
2024-08-27 05:48:22 +02:00
}
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if (overWidget == null)
2025-03-19 11:01:28 -04:00
{
2024-08-27 05:48:22 +02:00
SetLastMouseWidget(null);
mMouseStillTicks = -1;
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if (numOverWidgets > 1)
{
2019-08-23 11:56:54 -07:00
//int a = 0;
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
2025-03-19 11:01:28 -04:00
Debug.Assert(numOverWidgets <= 1);
2024-08-27 05:48:22 +02:00
}*/
2019-08-23 11:56:54 -07:00
public void FileRenamed(ProjectFileItem projectFileItem, String oldPath, String newPath)
{
String newFileName = scope String();
Path.GetFileName(newPath, newFileName);
for (var entry in mRecentlyDisplayedFiles)
{
if (Path.Equals(entry, oldPath))
{
entry.Set(newPath);
UpdateRecentFileMenuItems();
break;
}
}
2019-08-23 11:56:54 -07:00
RenameEditData(oldPath, newPath);
WithTabs(scope (tab) =>
2024-08-27 05:48:22 +02:00
{
var sourceViewPanel = tab.mContent as SourceViewPanel;
if (sourceViewPanel != null)
{
if (Path.Equals(sourceViewPanel.mFilePath, oldPath))
{
var sourceViewTab = (IDEApp.SourceViewTabButton)tab;
2024-08-27 05:48:22 +02:00
2019-08-23 11:56:54 -07:00
sourceViewPanel.PathChanged(newPath);
2024-08-27 05:48:22 +02:00
tab.Label = newFileName;
2022-04-02 07:25:17 -07:00
float newWidth = sourceViewTab.GetWantWidth();
if (newWidth != tab.mWantWidth)
{
tab.mWantWidth = newWidth;
tab.mTabbedView.mNeedResizeTabs = true;
}
2024-08-27 05:48:22 +02:00
}
}
});
2019-08-23 11:56:54 -07:00
var recentFileList = mRecentlyDisplayedFiles;
for (int32 recentFileIdx = 0; recentFileIdx < recentFileList.Count; recentFileIdx++)
{
2024-08-27 05:48:22 +02:00
if (recentFileList[recentFileIdx] == oldPath)
recentFileList[recentFileIdx].Set(newPath);
2019-08-23 11:56:54 -07:00
}
UpdateRecentDisplayedFilesMenuItems();
if (IsBeefFile(newPath) != IsBeefFile(oldPath))
{
if ((var projectSource = projectFileItem as ProjectSource) && (IsProjectSourceEnabled(projectSource)))
{
if (IsBeefFile(newPath))
{
mBfResolveCompiler.QueueProjectSource(projectSource, .None, false);
mBfBuildCompiler.QueueProjectSource(projectSource, .None, true);
2019-08-23 11:56:54 -07:00
}
else
{
mBfResolveCompiler.QueueProjectSourceRemoved(projectSource);
mBfBuildCompiler.QueueProjectSourceRemoved(projectSource);
}
mBfResolveCompiler.QueueDeferredResolveAll();
mBfResolveCompiler.QueueRefreshViewCommand();
}
}
}
public void OnWatchedFileChanged(ProjectItem projectItem, WatcherChangeTypes changeType, String newPath)
{
CompilerLog("IDEApp.OnWatchedFileChanged {} {} {}", projectItem.mName, changeType, newPath);
2020-01-12 09:21:50 -08:00
ProjectListViewItem listViewItem = null;
mProjectPanel?.mProjectToListViewMap.TryGetValue(projectItem, out listViewItem);
2019-08-23 11:56:54 -07:00
String newName = null;
if (newPath != null)
{
2024-08-27 05:48:22 +02:00
newName = scope:: String();
2019-08-23 11:56:54 -07:00
Path.GetFileName(newPath, newName);
}
if (changeType == .Renamed)
{
if (projectItem.mIncludeKind == .Auto)
{
2024-08-27 05:48:22 +02:00
let projectFileItem = projectItem as ProjectFileItem;
2019-08-23 11:56:54 -07:00
2020-01-12 09:21:50 -08:00
if (listViewItem != null)
listViewItem.Label = newName;
2019-08-23 11:56:54 -07:00
String oldPath = scope String();
projectFileItem.GetFullImportPath(oldPath);
projectFileItem.Rename(newName);
FileRenamed(projectFileItem, oldPath, newPath);
2020-01-12 09:21:50 -08:00
mProjectPanel?.SortItem((ProjectListViewItem)listViewItem.mParentItem);
2019-08-23 11:56:54 -07:00
}
}
else if (changeType == .Deleted)
{
if (projectItem.mIncludeKind == .Auto)
{
mProjectPanel?.DoDeleteItem(listViewItem, null, .ForceRemove);
2019-08-23 11:56:54 -07:00
}
}
else if (changeType == .FileCreated)
2024-08-27 05:48:22 +02:00
{
2019-08-23 11:56:54 -07:00
let projectFolder = projectItem as ProjectFolder;
if (projectFolder.IsAutoInclude())
{
if (!projectFolder.mChildMap.ContainsKey(newName))
{
let projectSource = new ProjectSource();
Path.GetFileName(newPath, projectSource.mName);
projectSource.mPath = new String(projectFolder.mPath);
projectSource.mPath.Append(Path.DirectorySeparatorChar);
projectSource.mPath.Append(projectSource.mName);
projectSource.mProject = projectFolder.mProject;
projectSource.mParentFolder = projectFolder;
projectFolder.AddChild(projectSource);
projectFolder.MarkAsUnsorted();
2020-01-12 09:21:50 -08:00
if (mProjectPanel != null)
{
mProjectPanel.AddProjectItem(projectSource);
mProjectPanel.QueueSortItem(listViewItem);
}
2019-08-23 11:56:54 -07:00
}
}
}
else if (changeType == .DirectoryCreated)
{
let projectFolder = projectItem as ProjectFolder;
if (projectFolder.IsAutoInclude())
{
if (!projectFolder.mChildMap.ContainsKey(newName))
{
let newFolder = new ProjectFolder();
Path.GetFileName(newPath, newFolder.mName);
newFolder.mPath = new String(projectFolder.mPath);
newFolder.mPath.Append(Path.DirectorySeparatorChar);
newFolder.mPath.Append(newFolder.mName);
newFolder.mProject = projectFolder.mProject;
newFolder.mParentFolder = projectFolder;
projectFolder.AddChild(newFolder);
projectFolder.MarkAsUnsorted();
2020-01-12 09:21:50 -08:00
if (mProjectPanel != null)
{
mProjectPanel.AddProjectItem(newFolder);
mProjectPanel.QueueSortItem(listViewItem);
}
2019-08-23 11:56:54 -07:00
newFolder.mAutoInclude = true;
2020-01-12 09:21:50 -08:00
if (mProjectPanel != null)
mProjectPanel.QueueRehupFolder(newFolder);
2019-08-23 11:56:54 -07:00
}
}
}
else if (changeType == .Failed)
{
if (mBfResolveCompiler?.HasRebuildFileWatches() == true)
{
mBfResolveCompiler.AddChangedDirectory("*");
mBfResolveCompiler.QueueDeferredResolveAll();
}
if (mBfBuildCompiler?.HasRebuildFileWatches() == true)
mBfBuildCompiler.AddChangedDirectory("*");
2020-01-12 09:21:50 -08:00
if (mProjectPanel != null)
{
if (let projectFolder = projectItem as ProjectFolder)
mProjectPanel.QueueRehupFolder(projectFolder);
}
// Manually scan project files for changes
mWorkspace.WithProjectItems(scope (projectItem) =>
{
var projectSource = projectItem as ProjectSource;
if (projectSource != null)
{
bool changed = false;
var editData = projectSource.mEditData;
if (editData != null)
{
if (File.GetLastWriteTime(editData.mFilePath) case .Ok(let fileTime))
{
if (fileTime != projectSource.mEditData.mFileTime)
2024-08-27 05:48:22 +02:00
{
if (!projectSource.mEditData.mMD5Hash.IsZero)
{
var text = scope String();
if (File.ReadAllText(editData.mFilePath, text, true) case .Ok)
{
var hash = MD5.Hash(.((.)text.Ptr, text.Length));
if (hash != projectSource.mEditData.mMD5Hash)
{
changed = true;
}
}
}
else
changed = true;
}
if (changed)
mFileWatcher.FileChanged(editData.mFilePath);
projectSource.mEditData.mFileTime = fileTime;
}
}
}
});
2019-08-23 11:56:54 -07:00
}
}
void UpdateWorkspace()
{
mFileWatcher.Update();
2020-09-18 05:30:21 -07:00
#if !CLI
mFileRecovery.Update();
#endif
2019-08-23 11:56:54 -07:00
bool appHasFocus = false;
for (var window in mWindows)
2024-08-27 05:48:22 +02:00
appHasFocus |= window.mHasFocus;
2019-08-23 11:56:54 -07:00
if ((appHasFocus) && (!mAppHasFocus))
{
CheckReloadSettings();
bool hadChange = false;
for (var entry in mWorkspace.mProjectFileEntries)
{
if (entry.HasFileChanged())
{
if (!hadChange)
{
String text = scope .();
if (entry.mProjectName != null)
text.AppendF($"The '{entry.mProjectName}' project file has been modified externally.");
else
text.Append("The workspace file has been modified externally.");
text.Append("\n\nDo you want to reload the workspace?");
var dialog = ThemeFactory.mDefault.CreateDialog("Reload Workspace?",
text);
dialog.AddYesNoButtons(new (dlg) =>
{
ReloadWorkspace();
},
new (dlg) =>
{
});
dialog.PopupWindow(GetActiveWindow());
hadChange = true;
}
}
}
}
mAppHasFocus = appHasFocus;
2019-08-23 11:56:54 -07:00
if (mRunningTestScript)
appHasFocus = true;
2024-08-27 05:48:22 +02:00
// Is this enough to get the behavior we want?
2019-08-23 11:56:54 -07:00
if (!appHasFocus)
return;
if (mLastFileChangeId != mFileWatcher.mChangeId)
{
if ((mFileChangedDialog != null) && (!mFileChangedDialog.mClosed))
{
mFileChangedDialog.Rehup();
}
mLastFileChangeId = mFileWatcher.mChangeId;
}
if (mSymbolReferenceHelper?.mKind == .Rename)
return;
2019-08-23 11:56:54 -07:00
var app = gApp;
Debug.Assert(app != null);
while (true)
{
using (mFileWatcher.mMonitor.Enter())
2019-08-23 11:56:54 -07:00
{
var dep = mFileWatcher.PopChangedDependency();
if (dep == null)
break;
var projectSourceDep = dep as ProjectSource;
if (projectSourceDep != null)
{
// We process these projectSources directly from the filename below
}
if (var path = dep as String)
{
StringView usePath = path;
if (usePath.EndsWith('*'))
usePath.RemoveFromEnd(1);
if (mBfResolveCompiler != null)
{
mBfResolveCompiler.AddChangedDirectory(usePath);
mBfResolveCompiler.QueueDeferredResolveAll();
}
if (mBfBuildCompiler != null)
mBfBuildCompiler.AddChangedDirectory(usePath);
}
2019-08-23 11:56:54 -07:00
}
}
2020-05-20 06:44:21 -07:00
if ((mFileChangedDialog != null) && (mFileChangedDialog.mDialogKind == .Deleted))
{
for (let fileName in mFileChangedDialog.mFileNames)
{
mFileWatcher.RemoveChangedFile(fileName);
}
}
2024-08-27 05:48:22 +02:00
while ( /*(appHasFocus) && */((mFileChangedDialog == null) || (mFileChangedDialog.mClosed)))
2019-08-23 11:56:54 -07:00
{
while (mExternalChangeDeferredOpen.Count > 0)
{
String filePath = mExternalChangeDeferredOpen.PopFront();
ShowSourceFile(filePath);
delete filePath;
}
var changedFile = mFileWatcher.PopChangedFile();
2024-08-27 05:48:22 +02:00
if (changedFile == null)
{
if (mFileChangedDialog != null)
mFileChangedDialog = null;
break;
}
2019-08-23 11:56:54 -07:00
String fileName = changedFile.mPath;
defer
{
delete changedFile;
}
#if IDE_C_SUPPORT
if ((IsClangSourceFile(fileName)) || (IsClangSourceHeader(fileName)))
{
mDepClang.mDoDependencyCheck = true;
}
#endif
FileChanged(fileName);
var editData = GetEditData(fileName, false, false);
if (editData == null)
continue;
//TODO: Check to see if this file is in the dependency list of the executing project
if (!editData.mProjectSources.IsEmpty)
mHaveSourcesChangedExternallySinceLastCompile = true;
2019-08-23 11:56:54 -07:00
editData.SetSavedData(null, IdSpan());
if (editData.mQueuedContent == null)
editData.mQueuedContent = new String();
editData.mQueuedContent.Clear();
2024-08-27 05:48:22 +02:00
if (LoadTextFile(fileName, editData.mQueuedContent, false, scope () =>
{
2019-11-22 12:27:13 -08:00
editData.BuildHash(editData.mQueuedContent);
}) case .Err(let err))
2019-08-23 11:56:54 -07:00
{
2021-12-31 10:26:32 -05:00
if (err case .OpenError(.SharingViolation))
2019-08-23 11:56:54 -07:00
{
// Put back on the list and try later
mFileWatcher.AddChangedFile(changedFile);
changedFile = null;
break;
}
editData.mFileDeleted = true;
}
2020-11-04 09:50:41 -08:00
editData.GetFileTime();
2019-08-23 11:56:54 -07:00
using (mMonitor.Enter())
{
for (var projectSource in editData.mProjectSources)
{
projectSource.HasChangedSinceLastCompile = true;
}
}
2024-08-27 05:48:22 +02:00
var sourceViewPanel = FindSourceViewPanel(fileName);
if (sourceViewPanel != null)
{
2019-08-23 11:56:54 -07:00
FileChangedDialog.DialogKind dialogKind = File.Exists(fileName) ? .Changed : .Deleted;
if ((sourceViewPanel.mEditData != null) && (sourceViewPanel.mEditData.mFileDeleted))
sourceViewPanel.mEditData.IsFileDeleted(); // Rehup
2024-08-27 05:48:22 +02:00
if ((sourceViewPanel.HasUnsavedChanges()) || (dialogKind == .Deleted))
{
if ((mFileChangedDialog != null) && (mFileChangedDialog.mApplyAllResult[(int)dialogKind].HasValue))
{
bool closeAll = mFileChangedDialog.mApplyAllResult[(int)dialogKind].Value;
if (closeAll)
2019-08-23 11:56:54 -07:00
gApp.CloseDocument(sourceViewPanel);
2024-08-27 05:48:22 +02:00
continue;
}
else
{
mFileChangedDialog = new FileChangedDialog();
mFileChangedDialog.Show(sourceViewPanel);
}
break;
}
else
{
sourceViewPanel.Reload();
2019-08-23 11:56:54 -07:00
//FileChanged(fileName);
2024-08-27 05:48:22 +02:00
}
}
2019-08-23 11:56:54 -07:00
else
{
editData.Reload();
FileChanged(editData);
}
}
if (mWorkspace.IsSingleFileWorkspace)
{
if (mWorkspace.mCompositeFile.HasPendingSave)
mWorkspace.mCompositeFile.Save();
}
}
public void FileChanged(String path)
{
OpenFileInSolutionDialog.ClearWriteTime(path);
}
public void FileChanged(FileEditData editData)
{
if (!IsBeefFile(editData.mFilePath))
return;
using (mMonitor.Enter())
{
for (var projectSource in editData.mProjectSources)
{
if (mBfResolveCompiler != null)
{
mBfResolveCompiler.QueueProjectSource(projectSource, .None, false);
2019-08-23 11:56:54 -07:00
mBfResolveCompiler.QueueDeferredResolveAll();
mBfResolveCompiler.QueueRefreshViewCommand();
}
}
}
}
#if !CLI && BF_PLATFORM_WINDOWS
2019-08-23 11:56:54 -07:00
void UpdateIPC()
2024-08-27 05:48:22 +02:00
{
2019-08-23 11:56:54 -07:00
bool hasFocus = false;
for (var window in mWindows)
{
if (window.mHasFocus)
hasFocus = true;
}
bool gotFocus = hasFocus && !mIPCHadFocus;
mIPCHadFocus = hasFocus;
//hasFocus = true;
if (gotFocus)
{
// Keep trying to create IPCHelper until we finally succeed
if (mIPCHelper == null)
2024-08-27 05:48:22 +02:00
{
2019-08-23 11:56:54 -07:00
mIPCHelper = new IPCHelper();
if (!mIPCHelper.Init("BF_IDE"))
2024-08-27 05:48:22 +02:00
DeleteAndNullify!(mIPCHelper);
2019-08-23 11:56:54 -07:00
else
{
#if !DEBUG
2024-08-27 05:48:22 +02:00
OutputLine("IPC: Hosting"); //
2019-08-23 11:56:54 -07:00
#endif
}
}
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
if (mIPCHelper != null)
{
mIPCHelper.Update();
var message = mIPCHelper.PopMessage();
if (message != null)
{
defer delete message;
var msgParts = String.StackSplit!(message, '|');
if (msgParts.Count >= 0)
{
String cmd = scope String(msgParts[0]);
if (cmd == "ShowFile")
{
// These loads any file changes before we try to do a goto
mMainWindow.SetForeground();
UpdateWorkspace();
String fileName = scope String(msgParts[1]);
int32 lineNum = int32.Parse(scope String(msgParts[2])).GetValueOrDefault();
var sourceViewPanel = ShowSourceFileLocation(fileName, -1, IDEApp.sApp.mWorkspace.GetHighestCompileIdx(), lineNum - 1, 0, LocatorType.Always, false);
if (sourceViewPanel != null)
sourceViewPanel.mWidgetWindow.SetForeground();
}
else if (cmd == "StopIPC")
{
#if !DEBUG
OutputLine("IPC: Stopping");
#endif
delete mIPCHelper;
mIPCHelper = null;
}
}
2024-08-27 05:48:22 +02:00
}
}
2019-08-23 11:56:54 -07:00
}
#endif
void DoLongUpdateCheck()
{
uint32 timeNow = Utils.GetTickCount();
if (mLastLongUpdateCheck != 0)
{
int ticksDiff = (int32)(timeNow - mLastLongUpdateCheck);
if (ticksDiff > 200)
{
int lastErrTicks = (int32)(timeNow - mLastLongUpdateCheckError);
if (lastErrTicks > 2000)
{
// Show this profile!
mLongUpdateProfileId.Dispose();
mLongUpdateProfileId = Profiler.StartSampling("LongUpdate");
mLastLongUpdateCheckError = timeNow;
}
}
}
Profiler.ClearSampling();
mLastLongUpdateCheck = Utils.GetTickCount();
}
public void SetDeferredUserRequest(DeferredUserRequest deferredUserRequest)
{
//Debug.WriteLine("SetDeferredUserRequest");
delete mDeferredUserRequest;
mDeferredUserRequest = deferredUserRequest;
}
public void ClearDeferredUserRequest()
{
//Debug.WriteLine("ClearDeferredUserRequest");
DeleteAndNullify!(mDeferredUserRequest);
}
void UpdateDeferredOpens()
{
void DoDeferredOpen(DeferredOpenKind openKind)
{
switch (openKind)
{
case .NewWorkspaceOrProject:
var dialog = ThemeFactory.mDefault.CreateDialog("Create Workspace?",
"Do you want to create an empty workspace or a new project?");
dialog.AddButton("Empty Workspace", new (evt) =>
{
mDeferredOpen = .NewWorkspace;
});
dialog.AddButton("New Project", new (evt) =>
{
CloseWorkspace();
FinishShowingNewWorkspace();
Cmd_NewProject();
});
dialog.PopupWindow(GetActiveWindow());
case .NewWorkspace:
if (mDeferredOpenFileName != null)
{
let directoryPath = scope String();
Path.GetDirectoryPath(mDeferredOpenFileName, directoryPath);
if (File.Exists(mDeferredOpenFileName))
{
2024-08-27 05:48:22 +02:00
var dialog = ThemeFactory.mDefault.CreateDialog("Already Exists",
scope String()..AppendF("A Beef workspace already exists at '{}'. Do you want to open it?", directoryPath),
DarkTheme.sDarkTheme.mIconWarning);
dialog.mDefaultButton = dialog.AddButton("Yes", new (evt) =>
{
2020-01-12 09:21:50 -08:00
DoOpenWorkspace(true);
});
dialog.AddButton("No", new (evt) =>
{
DeleteAndNullify!(mDeferredOpenFileName);
});
dialog.PopupWindow(GetActiveWindow());
return;
}
else if (!IDEUtils.IsDirectoryEmpty(directoryPath))
{
2024-08-27 05:48:22 +02:00
var dialog = ThemeFactory.mDefault.CreateDialog("Already Exists",
scope String()..AppendF("A non-empty directory already exists at '{}'. Are you sure you want to create a workspace there?", directoryPath),
DarkTheme.sDarkTheme.mIconWarning);
dialog.mDefaultButton = dialog.AddButton("Yes", new (evt) =>
{
2020-01-12 09:21:50 -08:00
DoOpenWorkspace(true);
});
dialog.AddButton("No", new (evt) =>
{
DeleteAndNullify!(mDeferredOpenFileName);
});
dialog.PopupWindow(GetActiveWindow());
return;
}
}
2020-01-12 09:21:50 -08:00
DoOpenWorkspace(true);
if (mDeferredOpen != .None)
mDeferredOpen = _;
case .Workspace:
DoOpenWorkspace();
case .CrashDump:
DoOpenCrashDump();
default:
}
}
var deferredOpen = mDeferredOpen;
mDeferredOpen = .None;
switch (deferredOpen)
{
case .File:
DoOpenFile();
case .CrashDump:
DoOpenCrashDump();
2024-08-27 05:48:22 +02:00
case .Workspace,.NewWorkspace,.NewWorkspaceOrProject:
if ((mDeferredOpenFileName == null) && (deferredOpen == .Workspace))
{
DoDeferredOpen(_);
break;
}
if (CheckCloseWorkspace(mMainWindow,
new () =>
{
DoDeferredOpen(_);
},
new () =>
{
DoDeferredOpen(_);
},
new () =>
{
DeleteAndNullify!(mDeferredOpenFileName);
}
))
{
DoDeferredOpen(_);
}
case .DebugSession:
DoOpenDebugSession();
default:
}
}
void VerifyModifiedBuffers()
{
if (mSymbolReferenceHelper?.mKind == .Rename)
return;
mWorkspace.WithProjectItems(scope (projectItem) =>
{
var projectSource = projectItem as ProjectSource;
if (projectSource != null)
{
if ((projectSource.mEditData != null) && (projectSource.mEditData.HasTextChanged()))
{
var sourceViewPanel = projectSource.mEditData?.mEditWidget.mPanel as SourceViewPanel;
Runtime.Assert(sourceViewPanel != null, "Source marked as modified with no SourceViewPanel");
}
}
});
}
2024-08-27 05:48:22 +02:00
public override void Update(bool batchStart)
{
2019-08-23 11:56:54 -07:00
scope AutoBeefPerf("IDEApp.Update");
/*using (mWorkspace.mMonitor.Enter())
{
}*/
if (mUpdateCnt % 120 == 0)
VerifyModifiedBuffers();
if (!mSettings.mSettingsValid)
Stop();
if (mWantShowOutput)
{
ShowOutput();
mWantShowOutput = false;
}
2019-08-23 11:56:54 -07:00
if (mDbgFastUpdate)
{
RefreshRate = 240;
MarkDirty();
}
else
{
RefreshRate = 60;
}
2024-08-27 05:48:22 +02:00
#if BF_PLATFORM_WINDOWS
2024-07-23 07:56:23 +02:00
if (mTerminalPanel != null)
{
// Detach terminal if the panel is closed
var terminalTabButton = GetTab(mTerminalPanel);
if (terminalTabButton == null)
{
mTerminalPanel.Detach();
}
}
2024-08-27 05:48:22 +02:00
#endif
2019-08-23 11:56:54 -07:00
bool hasFocus = false;
for (let window in mWindows)
{
hasFocus |= window.mHasFocus;
}
if (mEnableGCCollect)
{
/*if (hasFocus)
GC.SetAutoCollectPeriod(10);
else
GC.SetAutoCollectPeriod(2000);*/
}
while (!mDeferredFails.IsEmpty)
{
var error = mDeferredFails.PopFront();
Fail(error);
delete error;
}
if (mStopping)
return;
if (batchStart)
{
//BFApp_CheckMemory();
}
#if !CLI && BF_PLATFORM_WINDOWS
2019-09-18 08:13:00 -07:00
if (mSettings.mEnableDevMode)
UpdateIPC();
2019-08-23 11:56:54 -07:00
#endif
if (mRunTest)
{
if (mRunTestDelay > 0)
{
mRunTestDelay--;
}
else if (IsCompiling)
{
}
else if (mDebugger.mIsRunning)
2024-08-27 05:48:22 +02:00
{
mDebugger.Terminate();
2019-08-23 11:56:54 -07:00
mRunTestDelay = 30;
}
else
{
2024-08-27 05:48:22 +02:00
mTargetStartWithStep = false;
CompileAndRun(true);
2019-08-23 11:56:54 -07:00
mRunTestDelay = 200;
}
}
if (mProfilePanel != null)
mProfilePanel.ForcedUpdate();
if (mLongUpdateProfileId != 0)
DoLongUpdateCheck();
2024-10-21 09:18:07 -04:00
mGitManager.Update();
mPackMan.Update();
2024-08-27 05:48:22 +02:00
if (mWakaTime != null)
mWakaTime.Update();
2019-08-23 11:56:54 -07:00
if (mFindResultsPanel != null)
2024-08-27 05:48:22 +02:00
mFindResultsPanel.Update();
2019-08-23 11:56:54 -07:00
if (mBfResolveSystem != null)
2024-08-27 05:48:22 +02:00
mBfResolveSystem.PerfZoneStart("IDEApp.Update");
UpdateWorkspace();
2019-08-23 11:56:54 -07:00
if (ThemeFactory.mDefault != null)
2024-08-27 05:48:22 +02:00
ThemeFactory.mDefault.Update();
base.Update(batchStart);
DarkTooltipManager.UpdateTooltip();
mGlobalUndoManager.Update();
for (var project in mWorkspace.mProjects)
project.Update();
UpdateCompilersAndDebugger();
2020-07-31 06:39:39 -07:00
mDiagnosticsPanel?.UpdateStats();
2019-08-23 11:56:54 -07:00
if (mScriptManager != null)
mScriptManager.Update();
2024-08-27 05:48:22 +02:00
#if BF_PLATFORM_WINDOWS
2024-07-23 07:56:23 +02:00
if (mConsolePanel != null)
{
if ((mSettings.mAlwaysEnableConsole) ||
((mSettings.mDebugConsoleKind == .Embedded) && (mDebugger.mIsRunning)))
mConsolePanel.Attach();
else
mConsolePanel.Detach();
}
2024-08-27 05:48:22 +02:00
#endif
2019-08-23 11:56:54 -07:00
if (mTestManager != null)
{
mTestManager.Update();
if ((mTestManager.mIsDone) && (mTestManager.mQueuedOutput.IsEmpty))
2019-08-23 11:56:54 -07:00
{
if (mMainFrame != null)
mMainFrame.mStatusBar.SelectConfig(mTestManager.mPrevConfigName);
DeleteAndNullify!(mTestManager);
}
}
2024-08-27 05:48:22 +02:00
UpdateExecution();
2019-08-23 11:56:54 -07:00
if (mBfResolveSystem != null)
2024-08-27 05:48:22 +02:00
mBfResolveSystem.PerfZoneEnd();
2019-08-23 11:56:54 -07:00
UpdateDeferredOpens();
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
mHaveSourcesChangedInternallySinceLastCompile = false;
WithSourceViewPanels(scope (sourceViewPanel) =>
{
mHaveSourcesChangedInternallySinceLastCompile |= sourceViewPanel.mHasChangedSinceLastCompile;
});
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
if ((mCloseDelay > 0) && (--mCloseDelay == 0))
mMainWindow.Close();
2019-08-23 11:56:54 -07:00
ProcessIdleDeferredDeletes();
/*if ((mUpdateCnt % (60 * 3) == 0) && (mEnableGCCollect))
{
scope AutoBeefPerf("GC.Collect");
GC.Collect();
}*/
if (mDebugAutoShutdownCounter > 0)
{
if (--mDebugAutoShutdownCounter == 0)
Stop();
}
if ((mRunningTestScript) && (!IsCompiling) && (!AreTestsRunning()))
{
if ((mFailed) || (!mScriptManager.HasQueuedCommands))
{
if (++mScriptManager.mDoneTicks >= 2)
{
OutputLine("Total script time: {0}ms", mScriptManager.mTimeoutStopwatch.ElapsedMilliseconds);
if (mExitWhenTestScriptDone)
Stop();
else
mRunningTestScript = false;
}
}
}
for (var sourceViewPanel in mPostRemoveUpdatePanels)
{
if (sourceViewPanel.NeedsPostRemoveUpdate)
{
sourceViewPanel.Update();
}
else
{
//Debug.WriteLine("Removing sourceViewPanel from mPostRemoveUpdatePanel {0} from IDEApp.Update", sourceViewPanel);
sourceViewPanel.[Friend]mInPostRemoveUpdatePanels = false;
@sourceViewPanel.Remove();
if (sourceViewPanel.[Friend]mDeleteAfterPostRemoveUpdate)
DeferDelete(sourceViewPanel);
2019-08-23 11:56:54 -07:00
}
}
if (IDEApp.sApp.mSpellChecker != null)
IDEApp.sApp.mSpellChecker.CheckThreadDone();
if ((mDeferredShowSource != null) && (!mBfBuildCompiler.IsPerformingBackgroundOperation()) &&
(mBfResolveCompiler?.IsPerformingBackgroundOperation() != true))
{
var deferredShowSource = mDeferredShowSource;
mDeferredShowSource = null;
defer delete deferredShowSource;
ShowSourceFileLocation(deferredShowSource.mFilePath, deferredShowSource.mShowHotIdx, deferredShowSource.mRefHotIdx,
deferredShowSource.mLine, deferredShowSource.mColumn, deferredShowSource.mHilitePosition, deferredShowSource.mShowTemp);
}
2019-08-23 11:56:54 -07:00
///
//TODO: REMOVE
//mDebugger.InitiateHotResolve(.ActiveMethods | .Allocations);
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public override void Draw(bool forceDraw)
{
2019-08-23 11:56:54 -07:00
scope AutoBeefPerf("IDEApp.Draw");
#if CLI
return;
#endif
#unwarn
if (mBfResolveSystem != null)
{
2024-08-27 05:48:22 +02:00
mBfResolveSystem.PerfZoneStart("IDEApp.Draw");
base.Draw(forceDraw);
mBfResolveSystem.PerfZoneEnd();
if (mBfResolveSystem.mIsTiming)
{
mBfResolveSystem.StopTiming();
mBfResolveSystem.DbgPrintTimings();
}
2019-08-23 11:56:54 -07:00
}
else
2022-05-15 08:00:55 -07:00
base.Draw(forceDraw);
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
2024-08-27 05:48:22 +02:00
public void DrawSquiggle(Graphics g, float x, float y, float width)
{
2019-08-23 11:56:54 -07:00
Image squiggleImage = IDEApp.sApp.mSquiggleImage;
int32 segSize = 30;
float height = mSquiggleImage.mHeight;
2024-08-27 05:48:22 +02:00
float curX = x;
float curWidth = width;
2019-08-23 11:56:54 -07:00
float drawY = y + gApp.mCodeFont.GetHeight();
2024-08-27 05:48:22 +02:00
while (curWidth > 0)
{
float drawWidth = Math.Min(curWidth, segSize - (curX % segSize));
float u1 = ((int32)curX % segSize) / (float)squiggleImage.mSrcWidth;
float u2 = u1 + drawWidth / (float)squiggleImage.mSrcWidth;
g.DrawQuad(squiggleImage, curX, drawY, u1, 0, drawWidth, height, u2, 1.0f);
curWidth -= drawWidth;
curX += drawWidth;
}
}
public enum MessageBeepType
{
Default = -1,
Ok = 0x00000000,
Error = 0x00000010,
Question = 0x00000020,
Warning = 0x00000030,
Information = 0x00000040,
}
public static void Beep(MessageBeepType type)
{
2022-09-20 13:24:32 -07:00
#if BF_PLATFORM_WINDOWS && !CLI
2024-08-27 05:48:22 +02:00
MessageBeep(type);
#endif
}
2019-08-23 11:56:54 -07:00
#if BF_PLATFORM_WINDOWS
2024-08-27 05:48:22 +02:00
[Import("user32.lib"), CLink, CallingConvention(.Stdcall)]
public static extern bool MessageBeep(MessageBeepType type);
2019-08-23 11:56:54 -07:00
#endif
2024-08-27 05:48:22 +02:00
}
2019-08-23 11:56:54 -07:00
static
{
public static IDEApp gApp;
/*public static mixin GS(int val)
{
(int)(val * DarkTheme.sScale)
}
public static mixin GS(float val)
{
float fVal = val * DarkTheme.sScale;
fVal
}*/
}
}