mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-08 11:38:21 +02:00
13029 lines
378 KiB
Beef
13029 lines
378 KiB
Beef
using System;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using System.Collections;
|
|
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;
|
|
using IDE.ui;
|
|
using IDE.util;
|
|
|
|
[AttributeUsage(.Method, .ReflectAttribute | .AlwaysIncludeTarget, ReflectUser=.All)]
|
|
struct IDECommandAttribute : Attribute
|
|
{
|
|
|
|
}
|
|
|
|
namespace IDE
|
|
{
|
|
public class WindowData
|
|
{
|
|
public DrawLayer mOverlayLayer ~ delete _;
|
|
}
|
|
|
|
enum SourceShowType
|
|
{
|
|
ShowExisting,
|
|
ShowExistingInActivePanel,
|
|
New,
|
|
Temp,
|
|
}
|
|
|
|
enum Verbosity
|
|
{
|
|
Default,
|
|
Quiet,
|
|
Minimal,
|
|
Normal,
|
|
Detailed,
|
|
Diagnostic
|
|
}
|
|
|
|
class DeferredUserRequest
|
|
{
|
|
|
|
}
|
|
|
|
class DeferredShowPCLocation : DeferredUserRequest
|
|
{
|
|
public int32 mStackIdx;
|
|
|
|
public this(int32 stackIdx)
|
|
{
|
|
mStackIdx = stackIdx;
|
|
}
|
|
}
|
|
|
|
enum BeefVerb
|
|
{
|
|
None,
|
|
Open,
|
|
New,
|
|
OpenOrNew,
|
|
Test,
|
|
Run,
|
|
GetVersion
|
|
}
|
|
|
|
enum HotResolveState
|
|
{
|
|
None,
|
|
Pending,
|
|
PendingWithDataChanges
|
|
}
|
|
|
|
public class IDETabbedView : DarkTabbedView
|
|
{
|
|
public this(SharedData sharedData) : base(sharedData)
|
|
{
|
|
if (sharedData == null)
|
|
{
|
|
mSharedData.mOpenNewWindowDelegate.Add(new (fromTabbedView, newWindow) => gApp.SetupNewWindow(newWindow, true));
|
|
mSharedData.mTabbedViewClosed.Add(new (tabbedView) =>
|
|
{
|
|
if (tabbedView == gApp.mActiveDocumentsTabbedView)
|
|
gApp.mActiveDocumentsTabbedView = null;
|
|
});
|
|
}
|
|
}
|
|
|
|
public ~this()
|
|
{
|
|
if (gApp.mActiveDocumentsTabbedView == this)
|
|
gApp.mActiveDocumentsTabbedView = null;
|
|
}
|
|
|
|
public override TabbedView CreateTabbedView(SharedData sharedData)
|
|
{
|
|
IDETabbedView tabbedView = new IDETabbedView(sharedData);
|
|
return tabbedView;
|
|
}
|
|
}
|
|
|
|
public class IDEApp : BFApp
|
|
{
|
|
public static String sRTVersionStr = "042";
|
|
public const String cVersion = "0.42.6";
|
|
|
|
#if BF_PLATFORM_WINDOWS
|
|
public static readonly String sPlatform64Name = "Win64";
|
|
public static readonly String sPlatform32Name = "Win32";
|
|
#elif BF_PLATFORM_LINUX
|
|
public static readonly String sPlatform64Name = "Linux64";
|
|
public static readonly String sPlatform32Name = "Linux32";
|
|
#elif BF_PLATFORM_MACOS
|
|
public static readonly String sPlatform64Name = "macOS";
|
|
public static readonly String sPlatform32Name = null;
|
|
#else
|
|
public static readonly String sPlatform64Name = "Unknown64";
|
|
public static readonly String sPlatform32Name = "Unknown32";
|
|
#endif
|
|
|
|
public const uint32 cDialogOutlineLightColor = 0xFF404040;
|
|
public const uint32 cDialogOutlineDarkColor = 0xFF202020;
|
|
|
|
public static bool sExitTest;
|
|
|
|
public Verbosity mVerbosity = .Default;
|
|
public BeefVerb mVerb;
|
|
public bool mDbgCompileDump;
|
|
public int mDbgCompileIdx = -1;
|
|
public String mDbgCompileDir ~ delete _;
|
|
public String mDbgVersionedCompileDir ~ delete _;
|
|
public DateTime mDbgHighestTime;
|
|
public bool mForceFirstRun;
|
|
public bool mIsFirstRun;
|
|
public int? mTargetExitCode;
|
|
public FileVersionInfo mVersionInfo ~ delete _;
|
|
|
|
//public ToolboxPanel mToolboxPanel;
|
|
public Monitor mMonitor = new Monitor() ~ delete _;
|
|
public Thread mMainThread;
|
|
public PropertiesPanel mPropertiesPanel;
|
|
public Font mTinyCodeFont ~ delete _;
|
|
public Font mCodeFont ~ delete _;
|
|
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 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 _;
|
|
|
|
public WidgetWindow mMainWindow;
|
|
public DarkDockingFrame mDockingFrame;
|
|
public MainFrame mMainFrame;
|
|
public GlobalUndoManager mGlobalUndoManager = new GlobalUndoManager() ~ delete _;
|
|
public SourceControl mSourceControl = new SourceControl() ~ delete _;
|
|
|
|
public WidgetWindow mPopupWindow;
|
|
|
|
public IDETabbedView mActiveDocumentsTabbedView;
|
|
public static new IDEApp sApp;
|
|
|
|
public Image mTransparencyGridImage ~ delete _;
|
|
public Image mSquiggleImage ~ delete _;
|
|
public Image mCircleImage ~ delete _;
|
|
public bool mWantShowOutput;
|
|
|
|
public OutputPanel mOutputPanel;
|
|
public ImmediatePanel mImmediatePanel;
|
|
public FindResultsPanel mFindResultsPanel;
|
|
public WatchPanel mAutoWatchPanel;
|
|
public WatchPanel mWatchPanel;
|
|
public MemoryPanel mMemoryPanel;
|
|
public CallStackPanel mCallStackPanel;
|
|
public ErrorsPanel mErrorsPanel;
|
|
public BreakpointPanel mBreakpointPanel;
|
|
public ModulePanel mModulePanel;
|
|
public ThreadPanel mThreadPanel;
|
|
public ProfilePanel mProfilePanel;
|
|
public ProjectPanel mProjectPanel;
|
|
public ClassViewPanel mClassViewPanel;
|
|
public Widget mLastActivePanel;
|
|
public SourceViewPanel mLastActiveSourceViewPanel;
|
|
public AutoCompletePanel mAutoCompletePanel;
|
|
|
|
public Rect mRequestedWindowRect = Rect(64, 64, 1200, 1024);
|
|
public BFWindow.ShowKind mRequestedShowKind;
|
|
public WakaTime mWakaTime ~ delete _;
|
|
|
|
public Settings mSettings = new Settings() ~ delete _;
|
|
public Workspace mWorkspace = new Workspace() ~ delete _;
|
|
public FileWatcher mFileWatcher = new FileWatcher() ~ delete _;
|
|
public int mLastFileChangeId;
|
|
public bool mHaveSourcesChangedInternallySinceLastCompile;
|
|
public bool mHaveSourcesChangedExternallySinceLastCompile;
|
|
|
|
public String mConfigName = new String() ~ delete _;
|
|
public String mPlatformName = new String() ~ delete _;
|
|
|
|
public String mSetConfigName ~ delete _;
|
|
public String mSetPlatformName ~ delete _;
|
|
|
|
public Targets mTargets = new Targets() ~ delete _;
|
|
public DebugManager mDebugger ~ delete _;
|
|
public String mSymSrvStatus = new String() ~ delete _;
|
|
public delegate void(String) mPendingDebugExprHandler = null;
|
|
public bool mIsImmediateDebugExprEval;
|
|
public BookmarkManager mBookmarkManager = new BookmarkManager() ~ delete _;
|
|
public HistoryManager mHistoryManager = new HistoryManager() ~ delete _;
|
|
public Stopwatch mCompileAndRunStopwatch ~ delete _;
|
|
|
|
// 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
|
|
public BfSystem mBfBuildSystem ~ delete _;
|
|
public BfCompiler mBfBuildCompiler;
|
|
public int mCompileSinceCleanCount;
|
|
public BuildContext mBuildContext ~ delete _;
|
|
#if IDE_C_SUPPORT
|
|
public ClangCompiler mDepClang ~ delete _;
|
|
#endif
|
|
// The Beef resolve system is up-to-date with the projects' files,
|
|
// but the Clang resolver only has open files in it
|
|
public bool mNoResolve = false;
|
|
public BfSystem mBfResolveSystem ~ delete _;
|
|
public BfCompiler mBfResolveCompiler;
|
|
public BfResolveHelper mBfResolveHelper ~ delete _;
|
|
#if IDE_C_SUPPORT
|
|
public ClangCompiler mResolveClang;
|
|
#endif
|
|
public SpellChecker mSpellChecker;
|
|
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;
|
|
public bool mWantsClean = false;
|
|
public bool mWantsBeefClean = false;
|
|
public bool mWantsRehupCallstack = false;
|
|
public bool mDebugAutoBuild = false;
|
|
public bool mDebugAutoRun = false;
|
|
public bool mDisableBuilding;
|
|
public int32 mDebugAutoShutdownCounter;
|
|
public bool mTargetDidInitBreak = false;
|
|
public bool mTargetHadFirstBreak = false;
|
|
public bool mTargetStartWithStep = false;
|
|
public Breakpoint mMainBreakpoint;
|
|
public Breakpoint mMainBreakpoint2;
|
|
public bool mInDisassemblyView;
|
|
public bool mIsAttachPendingSourceShow;
|
|
public bool mStepOverExternalFiles;
|
|
|
|
public bool mRunningTestScript;
|
|
public bool mStartedWithTestScript;
|
|
public bool mExitWhenTestScriptDone = true;
|
|
public ScriptManager mScriptManager = new ScriptManager() ~ delete _;
|
|
public TestManager mTestManager ~ delete _;
|
|
public bool mExecutionPaused = false;
|
|
public HotResolveState mHotResolveState;
|
|
public int mHotResolveTryIdx;
|
|
public bool mDebuggerPerformingTask = false; // Executing an expression, loading from symbol server
|
|
public int32 mForegroundTargetCountdown = 0;
|
|
public int32 mDebuggerContinueIdx;
|
|
|
|
public SysMenu mWindowMenu;
|
|
public List<SourceViewPanel> mPostRemoveUpdatePanels = new .() ~ delete _;
|
|
public List<String> mRecentlyDisplayedFiles = new List<String>() ~ DeleteContainerAndItems!(_);
|
|
public List<SysMenu> mRecentlyDisplayedFilesMenuItems = new List<SysMenu>() ~ delete _;
|
|
public Dictionary<BFWindow, WindowData> mWindowDatas = new Dictionary<BFWindow, WindowData>() ~ delete _;
|
|
public Dictionary<String, FileEditData> mFileEditData = new Dictionary<String, FileEditData>() ~
|
|
{
|
|
for (var editDataPair in mFileEditData)
|
|
{
|
|
delete editDataPair.key;
|
|
editDataPair.value.Deref();
|
|
}
|
|
delete _;
|
|
};
|
|
public int32 mFileDataDataRevision;
|
|
|
|
/*public Point mLastAbsMousePos;
|
|
public Point mLastRelMousePos;
|
|
public int32 mMouseStillTicks;
|
|
public Widget mLastMouseWidget;*/
|
|
public int32 mCloseDelay;
|
|
public FileChangedDialog mFileChangedDialog;
|
|
public FindAndReplaceDialog mFindAndReplaceDialog;
|
|
public LaunchDialog mLaunchDialog;
|
|
public SymbolReferenceHelper mSymbolReferenceHelper;
|
|
public WrappedMenuValue mViewWhiteSpace = .(false);
|
|
public bool mEnableGCCollect = true;
|
|
public bool mDbgFastUpdate;
|
|
public bool mTestEnableConsole = false;
|
|
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;
|
|
|
|
#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 _;
|
|
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,
|
|
CrashDump,
|
|
DebugSession
|
|
}
|
|
DeferredOpenKind mDeferredOpen;
|
|
String mDeferredOpenFileName;
|
|
|
|
public class ExecutionCmd
|
|
{
|
|
public bool mOnlyIfNotFailed;
|
|
}
|
|
|
|
public class BuildCompletedCmd : ExecutionCmd
|
|
{
|
|
public Stopwatch mStopwatch ~ delete _;
|
|
public String mHotProjectName ~ delete _;
|
|
public bool mFailed;
|
|
#if IDE_C_SUPPORT
|
|
public HashSet<String> mClangCompiledFiles = new HashSet<String>() ~ DeleteContainerAndItems!(_);
|
|
#endif
|
|
}
|
|
|
|
class ProcessBfCompileCmd : ExecutionCmd
|
|
{
|
|
public BfPassInstance mBfPassInstance;
|
|
public CompileKind mCompileKind;
|
|
public Project mHotProject;
|
|
public Stopwatch mStopwatch ~ delete _;
|
|
public Profiler mProfiler;
|
|
public ProfileCmd mProfileCmd ~ delete _;
|
|
public bool mHadBeef;
|
|
|
|
public ~this()
|
|
{
|
|
}
|
|
}
|
|
|
|
class ProfileCmd : ExecutionCmd
|
|
{
|
|
public int mThreadId;
|
|
public String mDesc ~ delete _;
|
|
public int mSampleRate;
|
|
}
|
|
|
|
class StartDebugCmd : ExecutionCmd
|
|
{
|
|
public bool mWasCompiled;
|
|
public bool mHotCompileEnabled;
|
|
|
|
public this()
|
|
{
|
|
}
|
|
}
|
|
|
|
public class TargetCompletedCmd : ExecutionCmd
|
|
{
|
|
public Project mProject;
|
|
public bool mIsReady = true;
|
|
|
|
public this(Project project)
|
|
{
|
|
mProject = project;
|
|
}
|
|
|
|
public ~this()
|
|
{
|
|
|
|
}
|
|
}
|
|
|
|
public enum ArgsFileKind
|
|
{
|
|
None,
|
|
UTF8,
|
|
UTF16WithBom
|
|
}
|
|
|
|
public class ExecutionQueueCmd : ExecutionCmd
|
|
{
|
|
public String mFileName ~ delete _;
|
|
public String mArgs ~ delete _;
|
|
public String mWorkingDir ~ delete _;
|
|
public Dictionary<String, String> mEnvVars ~ DeleteDictionaryAndKeysAndItems!(_);
|
|
public ArgsFileKind mUseArgsFile;
|
|
public int32 mParallelGroup = -1;
|
|
public bool mIsTargetRun;
|
|
public String mStdInData ~ delete _;
|
|
public RunFlags mRunFlags;
|
|
}
|
|
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 _*/;
|
|
public Monitor mMonitor = new Monitor() ~ delete _;
|
|
public String mStdInData ~ delete _;
|
|
|
|
public Thread mOutputThread;
|
|
public Thread mErrorThread;
|
|
public Thread mInputThread;
|
|
|
|
public int? mExitCode;
|
|
public bool mAutoDelete = true;
|
|
public bool mCanceled;
|
|
public bool mIsTargetRun;
|
|
|
|
public ~this()
|
|
{
|
|
delete mProcess;
|
|
/*if (mProcess != null)
|
|
mProcess.Close();*/
|
|
|
|
if (mInputThread != null)
|
|
mInputThread.Join();
|
|
delete mInputThread;
|
|
|
|
if (mOutputThread != null)
|
|
mOutputThread.Join();
|
|
delete mOutputThread;
|
|
|
|
if (mErrorThread != null)
|
|
mErrorThread.Join();
|
|
delete mErrorThread;
|
|
|
|
delete mReadTask;
|
|
delete mOnReadTaskComplete;
|
|
delete mErrorTask;
|
|
delete mOnErrorTaskComplete;
|
|
}
|
|
|
|
public void Cancel()
|
|
{
|
|
mCanceled = true;
|
|
mProcess.Kill(0, .KillChildren);
|
|
}
|
|
}
|
|
List<ExecutionInstance> mExecutionInstances = new List<ExecutionInstance>() ~ DeleteContainerAndItems!(_);
|
|
public int32 mHotIndex = 0;
|
|
private int32 mStepCount;
|
|
private int32 mNoDebugMessagesTick;
|
|
|
|
public bool IsCompiling
|
|
{
|
|
get
|
|
{
|
|
return (mExecutionInstances.Count > 0) || (mExecutionQueue.Count > 0) || (mBuildContext != null);
|
|
}
|
|
}
|
|
|
|
public bool EnableGCCollect
|
|
{
|
|
get
|
|
{
|
|
return mEnableGCCollect;
|
|
}
|
|
|
|
set
|
|
{
|
|
mEnableGCCollect = value;
|
|
//GC.SetAutoCollectPeriod(mEnableGCCollect ? 2000 : -1);
|
|
GC.SetAutoCollectPeriod(mEnableGCCollect ? 20 : -1);
|
|
GC.SetCollectFreeThreshold(mEnableGCCollect ? 64*1024*1024 : -1);
|
|
}
|
|
}
|
|
|
|
[CallingConvention(.Stdcall),CLink]
|
|
static extern void IDEHelper_ProgramStart();
|
|
[CallingConvention(.Stdcall),CLink]
|
|
static extern void IDEHelper_ProgramDone();
|
|
|
|
public this()
|
|
{
|
|
sApp = this;
|
|
gApp = this;
|
|
mMainThread = Thread.CurrentThread;
|
|
|
|
IDEHelper_ProgramStart();
|
|
|
|
#if !CLI
|
|
mDebugger = new DebugManager();
|
|
mVerb = .OpenOrNew;
|
|
#endif
|
|
//BfParser_Go();///
|
|
|
|
mScriptManager.AddTarget(this);
|
|
}
|
|
|
|
public static this()
|
|
{
|
|
//Profiler.StartSampling();
|
|
}
|
|
|
|
public void GetBfSystems(List<BfSystem> systems)
|
|
{
|
|
systems.Add(mBfBuildSystem);
|
|
if (mBfResolveSystem != null)
|
|
systems.Add(mBfResolveSystem);
|
|
}
|
|
|
|
public void GetBfCompilers(List<BfCompiler> compiler)
|
|
{
|
|
if (mBfBuildCompiler != null)
|
|
compiler.Add(mBfBuildCompiler);
|
|
if (mBfResolveCompiler != null)
|
|
compiler.Add(mBfResolveCompiler);
|
|
}
|
|
|
|
public bool HaveSourcesChanged()
|
|
{
|
|
return mHaveSourcesChangedInternallySinceLastCompile || mHaveSourcesChangedExternallySinceLastCompile;
|
|
}
|
|
|
|
public void MarkWatchesDirty()
|
|
{
|
|
WithWatchPanels(scope (watchPanel) =>
|
|
{
|
|
watchPanel.MarkWatchesDirty(false);
|
|
});
|
|
}
|
|
|
|
void PCChanged()
|
|
{
|
|
var disasmPanel = TryGetDisassemblyPanel();
|
|
if (disasmPanel != null)
|
|
disasmPanel.mLocationDirty = true;
|
|
mDebugger.mCallStackDirty = true;
|
|
WithWatchPanels(scope (watchPanel) =>
|
|
{
|
|
watchPanel.MarkWatchesDirty(true, !mTargetHadFirstBreak);
|
|
});
|
|
mMemoryPanel.MarkViewDirty();
|
|
mCallStackPanel.MarkCallStackDirty();
|
|
mThreadPanel.MarkThreadsDirty();
|
|
mDebugger.CheckCallStack();
|
|
|
|
|
|
// 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;
|
|
|
|
ShowPCLocation(mDebugger.mActiveCallStackIdx, false, false, false, setFocus);
|
|
}
|
|
|
|
public ~this()
|
|
{
|
|
#if !CLI
|
|
if (!mStartedWithTestScript && !mForceFirstRun)
|
|
{
|
|
mSettings.Save();
|
|
SaveDefaultLayoutData();
|
|
}
|
|
#endif
|
|
|
|
/*WithTabs(scope (tabButton) =>
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
});*/
|
|
|
|
mixin RemoveAndDelete(var widget)
|
|
{
|
|
Widget.RemoveAndDelete(widget);
|
|
widget = null;
|
|
}
|
|
|
|
RemoveAndDelete!(mProjectPanel);
|
|
RemoveAndDelete!(mClassViewPanel);
|
|
RemoveAndDelete!(mOutputPanel);
|
|
RemoveAndDelete!(mImmediatePanel);
|
|
RemoveAndDelete!(mFindResultsPanel);
|
|
RemoveAndDelete!(mAutoWatchPanel);
|
|
RemoveAndDelete!(mWatchPanel);
|
|
RemoveAndDelete!(mMemoryPanel);
|
|
RemoveAndDelete!(mCallStackPanel);
|
|
RemoveAndDelete!(mBreakpointPanel);
|
|
RemoveAndDelete!(mModulePanel);
|
|
RemoveAndDelete!(mThreadPanel);
|
|
RemoveAndDelete!(mProfilePanel);
|
|
RemoveAndDelete!(mPropertiesPanel);
|
|
RemoveAndDelete!(mAutoCompletePanel);
|
|
|
|
if (mSymbolReferenceHelper != null)
|
|
mSymbolReferenceHelper.Close();
|
|
|
|
if (mBfBuildCompiler != null)
|
|
mBfBuildCompiler.CancelBackground();
|
|
if (mBfResolveCompiler != null)
|
|
mBfResolveCompiler.CancelBackground();
|
|
|
|
#if IDE_C_SUPPORT
|
|
mDepClang.CancelBackground();
|
|
mResolveClang.CancelBackground();
|
|
#endif
|
|
|
|
/*if (mMainBreakpoint != null)
|
|
{
|
|
mMainBreakpoint.Dispose();
|
|
mMainBreakpoint = null;
|
|
}*/
|
|
|
|
/*delete mBfBuildCompiler;
|
|
delete mBfBuildSystem;
|
|
delete mDepClang;
|
|
|
|
|
|
delete mBfResolveCompiler;
|
|
delete mBfResolveSystem;
|
|
delete mResolveClang;
|
|
|
|
delete mSpellChecker;
|
|
|
|
//base.Dispose();
|
|
|
|
delete mDebugger;
|
|
delete ThemeFactory.mDefault;*/
|
|
|
|
for (var val in mWindowDatas.Values)
|
|
delete val;
|
|
|
|
if (mErrorsPanel?.mParent != null)
|
|
mErrorsPanel.RemoveSelf();
|
|
|
|
ProcessIdleDeferredDeletes();
|
|
|
|
// Get rid of panels
|
|
ProcessDeferredDeletes();
|
|
|
|
delete mBfBuildCompiler;
|
|
delete mBfResolveCompiler;
|
|
#if IDE_C_SUPPORT
|
|
delete mResolveClang;
|
|
#endif
|
|
delete mSpellChecker;
|
|
|
|
//NOTE: this must be done after the resolve compiler has been destroyed
|
|
RemoveAndDelete!(mErrorsPanel);
|
|
|
|
// Clear these out, for delete ordering purposes
|
|
ProcessDeferredDeletes();
|
|
}
|
|
|
|
public bool IsCrashDump
|
|
{
|
|
get
|
|
{
|
|
return mCrashDumpPath != null;
|
|
}
|
|
}
|
|
|
|
void WithStandardPanels(delegate void(Panel panel) dlg)
|
|
{
|
|
dlg(mProjectPanel);
|
|
dlg(mClassViewPanel);
|
|
dlg(mOutputPanel);
|
|
dlg(mImmediatePanel);
|
|
dlg(mFindResultsPanel);
|
|
dlg(mAutoWatchPanel);
|
|
dlg(mWatchPanel);
|
|
dlg(mMemoryPanel);
|
|
dlg(mCallStackPanel);
|
|
dlg(mErrorsPanel);
|
|
dlg(mBreakpointPanel);
|
|
dlg(mModulePanel);
|
|
dlg(mThreadPanel);
|
|
dlg(mProfilePanel);
|
|
dlg(mPropertiesPanel);
|
|
dlg(mAutoCompletePanel);
|
|
}
|
|
|
|
public override void ShutdownCompleted()
|
|
{
|
|
base.ShutdownCompleted();
|
|
}
|
|
|
|
public override void Shutdown()
|
|
{
|
|
if ((mDebugger != null) && (mDebugger.mIsRunning))
|
|
{
|
|
mDebugger.DisposeNativeBreakpoints();
|
|
mDebugger.Detach();
|
|
mDebugger.mIsRunning = false;
|
|
mExecutionPaused = false;
|
|
}
|
|
|
|
base.Shutdown();
|
|
}
|
|
|
|
public override void Run()
|
|
{
|
|
base.Run();
|
|
}
|
|
|
|
public void Cmd_ViewNew()
|
|
{
|
|
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()))
|
|
#if IDE_C_SUPPORT
|
|
|| (mDepClang.HasQueuedCommands()) ||
|
|
(mResolveClang.HasQueuedCommands())
|
|
#endif
|
|
)
|
|
return;
|
|
|
|
for (var obj in mIdleDeferredDeletes)
|
|
delete obj;
|
|
mIdleDeferredDeletes.Clear();
|
|
}
|
|
|
|
public void DoOpenFile()
|
|
{
|
|
#if !CLI
|
|
String fullDir = scope .();
|
|
let sourceViewPanel = GetActiveSourceViewPanel();
|
|
if (sourceViewPanel != null)
|
|
{
|
|
Path.GetDirectoryPath(sourceViewPanel.mFilePath, fullDir);
|
|
}
|
|
else if ((gApp.mDebugger.mRunningPath != null) && (!mWorkspace.IsInitialized))
|
|
{
|
|
Path.GetDirectoryPath(gApp.mDebugger.mRunningPath, fullDir);
|
|
}
|
|
|
|
var fileDialog = scope OpenFileDialog();
|
|
fileDialog.Title = "Open File";
|
|
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
|
|
}
|
|
|
|
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);
|
|
|
|
LoadWorkspace(.OpenOrNew);
|
|
FinishShowingNewWorkspace();
|
|
}
|
|
|
|
public void DoOpenWorkspace(bool isCreating = false)
|
|
{
|
|
#if !CLI
|
|
if (mDeferredOpenFileName != null)
|
|
{
|
|
String prevFilePath = scope .(mDeferredOpenFileName);
|
|
mDeferredOpenFileName.Clear();
|
|
Path.GetActualPathName(prevFilePath, mDeferredOpenFileName);
|
|
OpenWorkspace(mDeferredOpenFileName);
|
|
DeleteAndNullify!(mDeferredOpenFileName);
|
|
return;
|
|
}
|
|
|
|
var folderDialog = scope FolderBrowserDialog(isCreating ? .Save : .Open);
|
|
var initialDir = scope String();
|
|
if (mInstallDir.Length > 0)
|
|
Path.GetDirectoryPath(.(mInstallDir, 0, mInstallDir.Length - 1), initialDir);
|
|
initialDir.Concat(Path.DirectorySeparatorChar, "Samples");
|
|
folderDialog.SelectedPath = initialDir;
|
|
//folderDialog.
|
|
if (folderDialog.ShowDialog(GetActiveWindow()).GetValueOrDefault() == .OK)
|
|
{
|
|
var selectedPath = scope String()..AppendF(folderDialog.SelectedPath);
|
|
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();
|
|
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()
|
|
{
|
|
#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;
|
|
mMainWindow.PreModalChild();
|
|
if (fileDialog.ShowDialog(GetActiveWindow()).GetValueOrDefault() == .OK)
|
|
{
|
|
for (String openFileName in fileDialog.FileNames)
|
|
{
|
|
String.NewOrSet!(mDeferredOpenFileName, openFileName);
|
|
mDeferredOpen =.DebugSession;
|
|
break;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
public void OpenCrashDump(StringView path)
|
|
{
|
|
AddRecentFile(.OpenedCrashDump, path);
|
|
|
|
StopDebugging();
|
|
String.NewOrSet!(mCrashDumpPath, path);
|
|
|
|
if (mDebugger.OpenMiniDump(mCrashDumpPath))
|
|
{
|
|
mDebugger.mIsRunning = true;
|
|
mExecutionPaused = false; // Make this false so we can detect a Pause immediately
|
|
}
|
|
else
|
|
{
|
|
Fail(StackStringFormat!("Failed to load minidump '{0}'", mCrashDumpPath));
|
|
DeleteAndNullify!(mCrashDumpPath);
|
|
}
|
|
}
|
|
|
|
public void DoOpenCrashDump()
|
|
{
|
|
#if !CLI
|
|
if (mDeferredOpenFileName != null)
|
|
{
|
|
OpenCrashDump(mDeferredOpenFileName);
|
|
DeleteAndNullify!(mDeferredOpenFileName);
|
|
return;
|
|
}
|
|
|
|
var fileDialog = scope OpenFileDialog();
|
|
fileDialog.Title = "Open Crash Dump";
|
|
fileDialog.SetFilter("Crash Dump (*.dmp)|*.dmp|All files (*.*)|*.*");
|
|
fileDialog.ValidateNames = true;
|
|
mMainWindow.PreModalChild();
|
|
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);
|
|
testPath.Append(Path.DirectorySeparatorChar);
|
|
testPath.Append(path);
|
|
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();
|
|
|
|
int32 char8Idx = int32.Parse(cmds[2]).GetValueOrDefault();
|
|
var sourceViewPanel = ShowSourceFile(cmds[1], null, SourceShowType.Temp);
|
|
if (sourceViewPanel == null)
|
|
return;
|
|
var editWidgetContent = sourceViewPanel.mEditWidget.mEditWidgetContent;
|
|
int line;
|
|
int lineChar;
|
|
editWidgetContent.GetLineCharAtIdx(char8Idx, out line, out lineChar);
|
|
sourceViewPanel.ShowFileLocation(char8Idx, .Always);
|
|
case "ShowCodeAddr":
|
|
var sourceViewPanel = GetActiveSourceViewPanel(true);
|
|
if (sourceViewPanel != null)
|
|
sourceViewPanel.RecordHistoryLocation();
|
|
|
|
int64 addr = int64.Parse(cmds[1], System.Globalization.NumberStyles.HexNumber).GetValueOrDefault();
|
|
String fileName = scope String();
|
|
int lineNum = 0;
|
|
int column;
|
|
int hotIdx;
|
|
int defLineStart;
|
|
int defLineEnd;
|
|
mDebugger.GetCodeAddrInfo((int)addr, fileName, out hotIdx, out defLineStart, out defLineEnd, out lineNum, out column);
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
public Dialog QuerySaveFiles(List<String> changedList, WidgetWindow window = null)
|
|
{
|
|
Dialog aDialog;
|
|
if (changedList.Count == 1)
|
|
{
|
|
aDialog = ThemeFactory.mDefault.CreateDialog("Save file?", StackStringFormat!("{0} has been modified. Save before closing?", changedList[0]), DarkTheme.sDarkTheme.mIconWarning);
|
|
}
|
|
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);
|
|
}
|
|
|
|
return aDialog;
|
|
}
|
|
|
|
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");
|
|
changedList.Add(fileName);
|
|
}
|
|
for (var project in mWorkspace.mProjects)
|
|
if (project.mHasChanged)
|
|
{
|
|
var fileName = new String();
|
|
Path.GetFileName(project.mProjectPath, fileName);
|
|
if (fileName.IsEmpty)
|
|
fileName.Append(project.IsDebugSession ? "Debug Session" : "Project");
|
|
changedList.Add(fileName);
|
|
}
|
|
mWorkspace.WithProjectItems(scope (projectItem) =>
|
|
{
|
|
var projectSource = projectItem as ProjectSource;
|
|
if (projectSource != null)
|
|
{
|
|
if ((projectSource.mEditData != null) && (projectSource.mEditData.HasTextChanged()))
|
|
{
|
|
var fileName = new String();
|
|
Path.GetFileName(projectSource.mPath, fileName);
|
|
changedList.Add(fileName);
|
|
}
|
|
}
|
|
|
|
});
|
|
WithSourceViewPanels(scope (sourceViewPanel) =>
|
|
{
|
|
if (sourceViewPanel.HasUnsavedChanges())
|
|
{
|
|
var fileName = scope String();
|
|
Path.GetFileName(sourceViewPanel.mFilePath, fileName);
|
|
if (!changedList.Contains(fileName))
|
|
changedList.Add(new String(fileName));
|
|
}
|
|
});
|
|
|
|
if (changedList.Count == 0)
|
|
{
|
|
DeleteDelegates();
|
|
if (!mRunningTestScript)
|
|
SaveWorkspaceUserData(false);
|
|
return true;
|
|
}
|
|
|
|
var aDialog = QuerySaveFiles(changedList);
|
|
DeleteAndClearItems!(changedList);
|
|
|
|
aDialog.mDefaultButton = aDialog.AddButton("Save", new (evt) =>
|
|
{
|
|
onSave();
|
|
} ~ delete onSave);
|
|
aDialog.AddButton("Don't Save", new (evt) =>
|
|
{
|
|
OutputLine("Changes not saved.");
|
|
onNoSave();
|
|
} ~ delete onNoSave);
|
|
|
|
aDialog.mEscButton = aDialog.AddButton("Cancel", new (evt) =>
|
|
{
|
|
if (onCancel != null)
|
|
onCancel();
|
|
} ~ delete onCancel);
|
|
aDialog.PopupWindow((WidgetWindow)window ?? mMainWindow);
|
|
return false;
|
|
}
|
|
|
|
public bool AllowClose(BFWindow window)
|
|
{
|
|
if (mRunningTestScript)
|
|
return true;
|
|
|
|
return CheckCloseWorkspace(window,
|
|
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);
|
|
}
|
|
|
|
public bool SecondaryAllowClose(BFWindow window)
|
|
{
|
|
if (mRunningTestScript)
|
|
return true;
|
|
|
|
List<String> changedList = scope List<String>();
|
|
defer ClearAndDeleteItems(changedList);
|
|
WithSourceViewPanels(scope (sourceViewPanel) =>
|
|
{
|
|
if ((sourceViewPanel.mWidgetWindow == window) && (sourceViewPanel.HasUnsavedChanges()))
|
|
{
|
|
var fileName = new String();
|
|
Path.GetFileName(sourceViewPanel.mFilePath, fileName);
|
|
changedList.Add(fileName);
|
|
}
|
|
});
|
|
if (changedList.Count == 0)
|
|
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;
|
|
WithSourceViewPanels(scope [&] (sourceViewPanel) =>
|
|
{
|
|
if ((!hadError) && (sourceViewPanel.mWidgetWindow == window) && (sourceViewPanel.HasUnsavedChanges()))
|
|
if (!_this.SaveFile(sourceViewPanel))
|
|
hadError = true;
|
|
});
|
|
if (hadError)
|
|
return;
|
|
mMainWindow.SetForeground();
|
|
window.Close(true);
|
|
});
|
|
aDialog.AddButton("Don't Save", new (evt) =>
|
|
{
|
|
mMainWindow.SetForeground();
|
|
window.Close(true);
|
|
});
|
|
|
|
aDialog.mEscButton = aDialog.AddButton("Cancel");
|
|
aDialog.PopupWindow((WidgetWindow)window ?? mMainWindow);
|
|
return false;
|
|
}
|
|
|
|
public SourceViewPanel GetActiveSourceViewPanel(bool includeLastActive = false)
|
|
{
|
|
if (mRunningTestScript)
|
|
return mLastActiveSourceViewPanel;
|
|
|
|
var activePanel = GetActiveDocumentPanel();
|
|
var sourceViewPanel = activePanel as SourceViewPanel;
|
|
if (sourceViewPanel != null)
|
|
return sourceViewPanel.GetActivePanel();
|
|
if ((mLastActiveSourceViewPanel != null) && (includeLastActive))
|
|
return mLastActiveSourceViewPanel.GetActivePanel();
|
|
return null;
|
|
}
|
|
|
|
public TextPanel GetActiveTextPanel()
|
|
{
|
|
var activePanel = GetActivePanel();
|
|
return activePanel as TextPanel;
|
|
}
|
|
|
|
public void RefreshVisibleViews(SourceViewPanel excludeSourceViewPanel = null)
|
|
{
|
|
WithSourceViewPanels(scope (sourceViewPanel) =>
|
|
{
|
|
if ((sourceViewPanel.mParent != null) && (sourceViewPanel != excludeSourceViewPanel))
|
|
sourceViewPanel.QueueFullRefresh(true);
|
|
});
|
|
}
|
|
|
|
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())
|
|
{
|
|
String path = scope String();
|
|
projectSource.GetFullImportPath(path);
|
|
String text = scope String();
|
|
projectSource.mEditData.mEditWidget.GetText(text);
|
|
if (!SafeWriteTextFile(path, text, true, projectSource.mEditData.mLineEndingKind, scope (outStr) =>
|
|
{
|
|
projectSource.mEditData.BuildHash(outStr);
|
|
}))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
mFileWatcher.OmitFileChange(path, text);
|
|
projectSource.mEditData.mLastFileTextVersion = projectSource.mEditData.mEditWidget.Content.mData.mCurTextVersionId;
|
|
#if IDE_C_SUPPORT
|
|
mDepClang.FileSaved(path);
|
|
#endif
|
|
|
|
if (mWakaTime != null)
|
|
mWakaTime.QueueFile(path, projectSource.mProject.mProjectName, true);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public bool SafeWriteTextFile(StringView path, StringView text, bool showErrors = true, LineEndingKind lineEndingKind = .Default, delegate void(StringView str) strOutDlg = null)
|
|
{
|
|
if (mWorkspace.IsSingleFileWorkspace)
|
|
{
|
|
if (mWorkspace.mCompositeFile.IsIncluded(path))
|
|
{
|
|
mWorkspace.mCompositeFile.Set(path, text);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
StringView useText = text;
|
|
|
|
Debug.Assert(!text.Contains('\r'));
|
|
|
|
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)
|
|
Fail(StackStringFormat!("Failed to write file '{0}'", path));
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (strOutDlg != null)
|
|
strOutDlg(useText);
|
|
|
|
return true;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
return Utils.LoadTextFile(fileName, outBuffer, autoRetry, onPreFilter);
|
|
}
|
|
|
|
public bool SaveFileAs(SourceViewPanel sourceViewPanel)
|
|
{
|
|
#if !CLI
|
|
SaveFileDialog dialog = scope .();
|
|
//dialog.ValidateNames = true;
|
|
|
|
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)
|
|
{
|
|
user.MarkDirty();
|
|
}
|
|
lineEndingKind = editData.mLineEndingKind;
|
|
}
|
|
|
|
// Lock file watcher to synchronize the 'file changed' notification so we don't
|
|
// think a file was externally saved
|
|
using (mFileWatcher.mMonitor.Enter())
|
|
{
|
|
String path = forcePath ?? editData.mFilePath;
|
|
String text = scope String();
|
|
editData.mEditWidget.GetText(text);
|
|
|
|
if (!SafeWriteTextFile(path, text, true, lineEndingKind, scope (outStr) =>
|
|
{
|
|
editData.BuildHash(outStr);
|
|
}))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
editData.mLastFileTextVersion = editData.mEditWidget.Content.mData.mCurTextVersionId;
|
|
mFileWatcher.OmitFileChange(path, text);
|
|
if (forcePath == null)
|
|
{
|
|
for (var user in editData.mEditWidget.mEditWidgetContent.mData.mUsers)
|
|
{
|
|
if (var sourceEditWidgetContent = user as SourceEditWidgetContent)
|
|
{
|
|
var sourceViewPanel = sourceEditWidgetContent.mSourceViewPanel;
|
|
sourceViewPanel?.FileSaved();
|
|
}
|
|
}
|
|
}
|
|
#if IDE_C_SUPPORT
|
|
mDepClang.FileSaved(path);
|
|
#endif
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public bool SaveFile(SourceViewPanel sourceViewPanel, String forcePath = null)
|
|
{
|
|
if ((sourceViewPanel.HasUnsavedChanges()) || (forcePath != null))
|
|
{
|
|
if ((forcePath == null) && (sourceViewPanel.mFilePath == null))
|
|
{
|
|
return SaveFileAs(sourceViewPanel);
|
|
}
|
|
|
|
if (sourceViewPanel.mEditData == null)
|
|
{
|
|
sourceViewPanel.mEditData = GetEditData(forcePath, true, false);
|
|
sourceViewPanel.mEditData.mEditWidget = sourceViewPanel.mEditWidget;
|
|
}
|
|
|
|
if (!SaveFile(sourceViewPanel.mEditData, forcePath))
|
|
return false;
|
|
|
|
if (sourceViewPanel.mProjectSource != null)
|
|
{
|
|
if (mWakaTime != null)
|
|
{
|
|
String path = forcePath ?? sourceViewPanel.mFilePath;
|
|
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;
|
|
}
|
|
|
|
///
|
|
|
|
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);
|
|
data.Add("ShowKind", window.mShowKind);
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (!serializeDocs)
|
|
{
|
|
if (tabWidget.mContent is SourceViewPanel)
|
|
continue;
|
|
if (tabWidget.mContent is DisassemblyPanel)
|
|
continue;
|
|
if (tabWidget.mContent is WelcomePanel)
|
|
continue;
|
|
}
|
|
|
|
using (data.CreateObject())
|
|
{
|
|
var panel = tabWidget.mContent as Panel;
|
|
if (!panel.WantsSerialization)
|
|
continue;
|
|
|
|
if (tabWidget.mIsActive)
|
|
data.Add("Active", true);
|
|
|
|
data.Add("TabLabel", tabWidget.mLabel);
|
|
data.Add("TabWidth", tabWidget.mWantWidth / DarkTheme.sScale);
|
|
|
|
if (var watchPanel = tabWidget.mContent as WatchPanel)
|
|
{
|
|
watchPanel.Serialize(data, serializeDocs);
|
|
}
|
|
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);
|
|
if (!dockedWidget.mAutoClose)
|
|
data.Add("Permanent", true);
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SaveDefaultLayoutData()
|
|
{
|
|
if (mMainWindow == null)
|
|
return true;
|
|
|
|
StructuredData sd = scope StructuredData();
|
|
|
|
sd.CreateNew();
|
|
sd.Add("FileVersion", 1);
|
|
|
|
using (sd.CreateObject("MainWindow"))
|
|
{
|
|
SerializeWindow(sd, mMainWindow);
|
|
}
|
|
|
|
using (sd.CreateObject("MainDockingFrame"))
|
|
{
|
|
SerializeDockingFrame(sd, mDockingFrame, false);
|
|
}
|
|
|
|
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);
|
|
|
|
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"))
|
|
{
|
|
if (mMainWindow != null)
|
|
SerializeWindow(sd, mMainWindow);
|
|
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);
|
|
}
|
|
}
|
|
|
|
using (sd.CreateObject("MainDockingFrame"))
|
|
{
|
|
SerializeDockingFrame(sd, mDockingFrame, true);
|
|
}
|
|
|
|
using (sd.CreateArray("RecentFilesList"))
|
|
{
|
|
for (var recentFile in mRecentlyDisplayedFiles)
|
|
sd.Add(recentFile);
|
|
}
|
|
|
|
using (sd.CreateArray("Breakpoints"))
|
|
{
|
|
for (var breakpoint in mDebugger.mBreakpointList)
|
|
{
|
|
if ((breakpoint.mFileName != null) || (breakpoint.mIsMemoryBreakpoint) || (breakpoint.mSymbol != null))
|
|
{
|
|
using (sd.CreateObject())
|
|
{
|
|
if (breakpoint.mFileName != null)
|
|
{
|
|
sd.Add("File", breakpoint.mFileName);
|
|
sd.Add("Line", breakpoint.mLineNum);
|
|
sd.Add("Column", breakpoint.mColumn);
|
|
if (breakpoint.mInstrOffset != -1)
|
|
sd.Add("InstrOffset", breakpoint.mInstrOffset);
|
|
}
|
|
if (breakpoint.mSymbol != null)
|
|
sd.Add("Symbol", breakpoint.mSymbol);
|
|
if (breakpoint.mIsMemoryBreakpoint)
|
|
sd.Add("MemoryWatchExpression", breakpoint.mMemoryWatchExpression);
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
using (sd.CreateArray("Bookmarks"))
|
|
{
|
|
for (var bookmark in mBookmarkManager.mBookmarkList)
|
|
{
|
|
if (bookmark.mFileName != null)
|
|
{
|
|
using (sd.CreateObject())
|
|
{
|
|
sd.Add("File", bookmark.mFileName);
|
|
sd.Add("Line", bookmark.mLineNum);
|
|
sd.Add("Column", bookmark.mColumn);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
using (sd.CreateObject("DebuggerDisplayTypes"))
|
|
{
|
|
var displayTypeNames = scope String();
|
|
mDebugger.GetDisplayTypeNames(displayTypeNames);
|
|
var referenceIds = String.StackSplit!(displayTypeNames, '\n');
|
|
for (var referenceId in referenceIds)
|
|
{
|
|
if (!referenceId.StartsWith("0", StringComparison.Ordinal))
|
|
{
|
|
DebugManager.IntDisplayType intDisplayType;
|
|
DebugManager.MmDisplayType mmDisplayType;
|
|
DebugManager.FloatDisplayType floatDisplayType;
|
|
mDebugger.GetDisplayTypes(referenceId, out intDisplayType, out mmDisplayType, out floatDisplayType);
|
|
using (sd.CreateObject(referenceId))
|
|
{
|
|
sd.ConditionalAdd("IntDisplayType", intDisplayType);
|
|
sd.ConditionalAdd("MmDisplayType", mmDisplayType);
|
|
sd.ConditionalAdd("FloatDisplayType", floatDisplayType);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
using (sd.CreateArray("StepFilters"))
|
|
{
|
|
for (var stepFilter in mDebugger.mStepFilterList.Values)
|
|
{
|
|
if (stepFilter.mIsGlobal)
|
|
continue;
|
|
if (stepFilter.mKind == .Filtered)
|
|
sd.Add(stepFilter.mFilter);
|
|
}
|
|
sd.RemoveIfEmpty();
|
|
}
|
|
|
|
using (sd.CreateArray("StepNotFilters"))
|
|
{
|
|
for (var stepFilter in mDebugger.mStepFilterList.Values)
|
|
{
|
|
if (stepFilter.mIsGlobal)
|
|
continue;
|
|
if (stepFilter.mKind == .NotFiltered)
|
|
sd.Add(stepFilter.mFilter);
|
|
}
|
|
sd.RemoveIfEmpty();
|
|
}
|
|
}
|
|
|
|
bool SaveWorkspaceUserData(bool showErrors = true)
|
|
{
|
|
// Don't save if we didn't finish creating the workspace
|
|
if (mWorkspace.mNeedsCreate)
|
|
return false;
|
|
if (!mWorkspace.IsInitialized)
|
|
return false;
|
|
|
|
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;
|
|
}
|
|
|
|
StructuredData sd = scope StructuredData();
|
|
|
|
sd.CreateNew();
|
|
SaveWorkspaceUserData(sd);
|
|
|
|
String projectUserFileName = scope String();
|
|
GetWorkspaceUserDataFileName(projectUserFileName);
|
|
String jsonString = scope String();
|
|
sd.ToTOML(jsonString);
|
|
|
|
if (projectUserFileName.IsEmpty)
|
|
return false;
|
|
|
|
return SafeWriteTextFile(projectUserFileName, jsonString, showErrors);
|
|
}
|
|
|
|
bool GetWorkspaceUserDataFileName(String outResult)
|
|
{
|
|
if (mWorkspace.mDir == null)
|
|
return false;
|
|
|
|
if (mWorkspace.mCompositeFile != null)
|
|
{
|
|
outResult.Append(mWorkspace.mCompositeFile.mFilePath, ".bfuser");
|
|
}
|
|
else
|
|
{
|
|
outResult.Append(mWorkspace.mDir, "/BeefSpace_User.toml");
|
|
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;
|
|
}
|
|
|
|
void GetDefaultLayoutDataFileName(String outResult)
|
|
{
|
|
outResult.Append(mInstallDir, "/DefaultLayout.toml");
|
|
}
|
|
|
|
bool GetWorkspaceFileName(String outResult)
|
|
{
|
|
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;
|
|
}
|
|
|
|
///
|
|
|
|
bool SaveWorkspace()
|
|
{
|
|
if (mWorkspace.mNeedsCreate)
|
|
mWorkspace.mNeedsCreate = false;
|
|
|
|
if (!mWorkspace.IsSingleFileWorkspace)
|
|
{
|
|
#if !CLI
|
|
if (mWorkspace.mDir == null)
|
|
{
|
|
let activeWindow = GetActiveWindow();
|
|
|
|
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);
|
|
|
|
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)
|
|
{
|
|
Fail(StackStringFormat!("Failed to create workspace directory '{0}'", mWorkspace.mDir));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
StructuredData data = scope StructuredData();
|
|
data.CreateNew();
|
|
mWorkspace.Serialize(data);
|
|
|
|
String tomlString = scope String();
|
|
data.ToTOML(tomlString);
|
|
if (!mWorkspace.IsSingleFileWorkspace)
|
|
{
|
|
String workspaceFileName = scope String();
|
|
GetWorkspaceFileName(workspaceFileName);
|
|
|
|
if (!SafeWriteTextFile(workspaceFileName, tomlString))
|
|
{
|
|
Fail(StackStringFormat!("Failed to write workspace file '{0}'", workspaceFileName));
|
|
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();
|
|
return true;
|
|
}
|
|
|
|
bool SaveDebugSession()
|
|
{
|
|
let project = mWorkspace.mProjects[0];
|
|
|
|
#if !CLI
|
|
if (project.mProjectPath.IsEmpty)
|
|
{
|
|
SaveFileDialog dialog = scope .();
|
|
let activeWindow = GetActiveWindow();
|
|
|
|
let workspaceOptions = GetCurWorkspaceOptions();
|
|
let options = GetCurProjectOptions(project);
|
|
|
|
String execCmd = scope .();
|
|
ResolveConfigString(mPlatformName, workspaceOptions, project, options, options.mDebugOptions.mCommand, "command", execCmd);
|
|
|
|
if (!execCmd.IsWhiteSpace)
|
|
{
|
|
String initialDir = scope .();
|
|
Path.GetDirectoryPath(execCmd, initialDir).IgnoreError();
|
|
if (!initialDir.IsWhiteSpace)
|
|
dialog.InitialDirectory = initialDir;
|
|
dialog.SetFilter("Debug Session (*.bfdbg)|*.bfdbg");
|
|
dialog.DefaultExt = ".bfdbg";
|
|
|
|
String fileName = scope .();
|
|
Path.GetFileNameWithoutExtension(execCmd, fileName);
|
|
if (!fileName.IsEmpty)
|
|
{
|
|
fileName.Append(".bfdbg");
|
|
dialog.FileName = fileName;
|
|
}
|
|
}
|
|
|
|
dialog.OverwritePrompt = true;
|
|
if (dialog.ShowDialog(activeWindow).GetValueOrDefault() != .OK)
|
|
return false;
|
|
|
|
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;*/
|
|
}
|
|
|
|
public void AddProjectToWorkspace(Project project, bool addToProjectList = true)
|
|
{
|
|
if (addToProjectList)
|
|
mWorkspace.mProjects.Add(project);
|
|
mBfBuildSystem.AddProject(project);
|
|
#if !CLI
|
|
if (mBfResolveSystem != null)
|
|
mBfResolveSystem.AddProject(project);
|
|
#endif
|
|
mWorkspace.ClearProjectNameCache();
|
|
}
|
|
|
|
public void AddNewProjectToWorkspace(Project project, VerSpecRecord verSpec = null)
|
|
{
|
|
AddProjectToWorkspace(project);
|
|
mWorkspace.SetChanged();
|
|
|
|
var relPath = scope String();
|
|
Path.GetRelativePath(project.mProjectPath, mWorkspace.mDir, relPath);
|
|
relPath.Replace("\\", "/");
|
|
|
|
int lastSlash = relPath.LastIndexOf('/');
|
|
if (lastSlash != -1)
|
|
relPath.RemoveToEnd(lastSlash);
|
|
|
|
/*var endStr = "/BeefProj.toml";
|
|
if (relPath.EndsWith(endStr))
|
|
relPath.RemoveToEnd(relPath.Length - endStr.Length);*/
|
|
|
|
var projectSpec = new Workspace.ProjectSpec();
|
|
projectSpec.mProjectName = new .(project.mProjectName);
|
|
if (verSpec != null)
|
|
{
|
|
projectSpec.mVerSpec = verSpec;
|
|
}
|
|
else
|
|
{
|
|
projectSpec.mVerSpec = new .();
|
|
projectSpec.mVerSpec.SetPath(relPath);
|
|
}
|
|
mWorkspace.mProjectSpecs.Add(projectSpec);
|
|
|
|
var dep = new Project.Dependency();
|
|
dep.mProjectName = new .("corlib");
|
|
dep.mVerSpec = new .();
|
|
dep.mVerSpec.SetSemVer("*");
|
|
project.mDependencies.Add(dep);
|
|
|
|
for (var checkProject in mWorkspace.mProjects)
|
|
{
|
|
int idx = checkProject.mDependencies.FindIndex(scope (dep) => dep.mProjectName == project.mProjectName);
|
|
if (idx != -1)
|
|
ProjectOptionsChanged(checkProject);
|
|
}
|
|
ProjectOptionsChanged(project, false);
|
|
}
|
|
|
|
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);
|
|
project.mProjectPath.Append("BeefProj.toml");
|
|
project.mProjectDir.Set(projDir);
|
|
project.mGeneralOptions.mTargetType = targetType;
|
|
project.SetupDefault();
|
|
project.SetChanged();
|
|
AddNewProjectToWorkspace(project);
|
|
project.FinishCreate();
|
|
|
|
mProjectPanel.InitProject(project);
|
|
mProjectPanel.Sort();
|
|
mWorkspace.FixOptions();
|
|
mWorkspace.mHasChanged = true;
|
|
|
|
mWorkspace.ClearProjectNameCache();
|
|
CurrentWorkspaceConfigChanged();
|
|
return project;
|
|
}
|
|
|
|
void StopDebugging()
|
|
{
|
|
if (mDebugger.mIsRunning)
|
|
{
|
|
mDebugger.StopDebugging();
|
|
while (mDebugger.GetRunState() != .Terminated)
|
|
{
|
|
mDebugger.Update();
|
|
}
|
|
mDebugger.mIsRunning = false;
|
|
mExecutionPaused = false;
|
|
}
|
|
mDebugger.DisposeNativeBreakpoints();
|
|
mWantsRehupCallstack = false;
|
|
}
|
|
|
|
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)
|
|
{
|
|
mBfResolveSystem = new BfSystem();
|
|
mBfResolveCompiler = mBfResolveSystem.CreateCompiler(true);
|
|
mBfResolveHelper = new BfResolveHelper();
|
|
}
|
|
|
|
mCompileSinceCleanCount = 0;
|
|
mBfBuildSystem = new BfSystem();
|
|
mBfBuildCompiler = mBfBuildSystem.CreateCompiler(false);
|
|
mBfBuildCompiler.ClearBuildCache();
|
|
|
|
/*var docPanels = scope List<Widget>();
|
|
WithTabs(scope [&] (tab) =>
|
|
{
|
|
var sourceViewPanel = tab.mContent as SourceViewPanel;
|
|
if (sourceViewPanel != null)
|
|
{
|
|
docPanels.Add(sourceViewPanel);
|
|
}
|
|
});
|
|
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();
|
|
}
|
|
|
|
if (!mRunningTestScript)
|
|
{
|
|
mActiveDocumentsTabbedView = null;
|
|
WithStandardPanels(scope (panel) =>
|
|
{
|
|
ResetPanel(panel);
|
|
});
|
|
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;
|
|
|
|
delete mWorkspace;
|
|
mWorkspace = new Workspace();
|
|
|
|
mErrorsPanel.Clear();
|
|
|
|
OutputLine("Workspace closed.");
|
|
}
|
|
|
|
void FinishShowingNewWorkspace(bool loadUserData = true)
|
|
{
|
|
if ((loadUserData) && (!mRunningTestScript))
|
|
{
|
|
if (!LoadWorkspaceUserData())
|
|
{
|
|
CreateDefaultLayout();
|
|
}
|
|
}
|
|
|
|
WorkspaceLoaded();
|
|
UpdateTitle();
|
|
UpdateRecentDisplayedFilesMenuItems();
|
|
|
|
mProjectPanel.RebuildUI();
|
|
|
|
ShowPanel(mOutputPanel, false);
|
|
mMainFrame.RehupSize();
|
|
|
|
ShowStartupFile();
|
|
}
|
|
|
|
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();
|
|
}
|
|
},
|
|
new () =>
|
|
{
|
|
CloseWorkspaceAndSetupNew();
|
|
}, null);
|
|
|
|
if (done)
|
|
{
|
|
CloseWorkspaceAndSetupNew();
|
|
}
|
|
|
|
//CloseWorkspace();
|
|
//FinishShowingNewWorkspace();
|
|
}
|
|
|
|
[IDECommand]
|
|
void DuplicateLine()
|
|
{
|
|
var sewc = GetActiveSourceEditWidgetContent();
|
|
if (sewc != null)
|
|
sewc.DuplicateLine();
|
|
}
|
|
|
|
[IDECommand]
|
|
void CommentSelection()
|
|
{
|
|
var sewc = GetActiveSourceEditWidgetContent();
|
|
if (sewc != null)
|
|
sewc.ToggleComment(true);
|
|
}
|
|
|
|
[IDECommand]
|
|
void UncommentSelection()
|
|
{
|
|
var sewc = GetActiveSourceEditWidgetContent();
|
|
if (sewc != null)
|
|
sewc.ToggleComment(false);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
void FlushDeferredLoadProjects()
|
|
{
|
|
while (true)
|
|
{
|
|
bool hadLoad = false;
|
|
for (int projectIdx = 0; projectIdx < mWorkspace.mProjects.Count; projectIdx++)
|
|
{
|
|
var project = mWorkspace.mProjects[projectIdx];
|
|
if (project.mLoadDeferred)
|
|
{
|
|
hadLoad = true;
|
|
|
|
var projectPath = project.mProjectPath;
|
|
if (!project.Load(projectPath))
|
|
{
|
|
OutputErrorLine("Failed to load project '{0}' from '{1}'", project.mProjectName, projectPath);
|
|
LoadFailed();
|
|
project.mFailed = true;
|
|
}
|
|
|
|
AddProjectToWorkspace(project, false);
|
|
}
|
|
}
|
|
if (!hadLoad)
|
|
break;
|
|
}
|
|
}
|
|
|
|
protected void LoadWorkspace(BeefVerb verb)
|
|
{
|
|
scope AutoBeefPerf("IDEApp.LoadWorkspace");
|
|
|
|
AddRecentFile(.OpenedWorkspace, mWorkspace.mDir);
|
|
|
|
StructuredData data = null;
|
|
|
|
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
|
|
GetWorkspaceFileName(workspaceFileName);
|
|
data = scope StructuredData(); //.LoadFromFile(workspaceFileName).GetValueOrDefault();
|
|
|
|
LoadConfig();
|
|
|
|
bool isNew = false;
|
|
bool wantSave = false;
|
|
|
|
if (StructuredLoad(data, workspaceFileName) case .Err(let err))
|
|
{
|
|
mBeefConfig.Refresh();
|
|
|
|
switch (err)
|
|
{
|
|
case .FormatError(int lineNum):
|
|
OutputErrorLine("Workspace format error in '{0}' on line {1}", workspaceFileName, lineNum);
|
|
LoadFailed();
|
|
return;
|
|
case .FileError: // Assume 'file not found'
|
|
if (verb == .OpenOrNew)
|
|
{
|
|
isNew = true;
|
|
}
|
|
else if (verb == .New)
|
|
{
|
|
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();
|
|
//int lastSlashPos = mWorkspace.mDir.LastIndexOf(IDEUtils.cNativeSlash);
|
|
String projectName = mWorkspace.mName;
|
|
Debug.Assert(!projectName.IsWhiteSpace);
|
|
|
|
String projectPath = scope String(mWorkspace.mDir);
|
|
Utils.GetDirWithSlash(projectPath);
|
|
projectPath.Append("BeefProj.toml");
|
|
|
|
Project project = new Project();
|
|
mWorkspace.mProjects.Add(project);
|
|
project.mProjectPath.Set(projectPath);
|
|
if (!project.Load(projectPath))
|
|
{
|
|
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);
|
|
|
|
project.FinishCreate(false);
|
|
|
|
var verSpec = new VerSpecRecord();
|
|
verSpec.SetSemVer("*");
|
|
|
|
switch (AddProject("corlib", verSpec))
|
|
{
|
|
case .Ok(let libProject):
|
|
var dep = new Project.Dependency();
|
|
dep.mProjectName = new String("corlib");
|
|
dep.mVerSpec = verSpec;
|
|
project.mDependencies.Add(dep);
|
|
default:
|
|
delete verSpec;
|
|
}
|
|
}
|
|
|
|
var projSpec = new Workspace.ProjectSpec();
|
|
projSpec.mProjectName = new String(project.mProjectName);
|
|
projSpec.mVerSpec = new VerSpecRecord();
|
|
projSpec.mVerSpec.SetPath(".");
|
|
mWorkspace.mProjectSpecs.Add(projSpec);
|
|
|
|
mWorkspace.mStartupProject = project;
|
|
mWorkspace.mHasChanged = true;
|
|
AddProjectToWorkspace(project, false);
|
|
|
|
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);
|
|
if (wantSave)
|
|
{
|
|
SaveWorkspace();
|
|
project.Save();
|
|
}
|
|
else
|
|
{
|
|
mWorkspace.mNeedsCreate = true;
|
|
OutputLine("Use 'File\\Save All' to commit to disk.");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (mVerb == .New)
|
|
{
|
|
OutputErrorLine("Workspace '{0}' already exists, but '-new' argument was specified.", workspaceFileName);
|
|
LoadFailed();
|
|
}
|
|
|
|
var startupProjectName = scope String();
|
|
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"))
|
|
{
|
|
var projSpec = new Workspace.ProjectSpec();
|
|
projSpec.mProjectName = new String(projectName);
|
|
projSpec.mVerSpec = new VerSpecRecord();
|
|
mWorkspace.mProjectSpecs.Add(projSpec);
|
|
|
|
if (projSpec.mVerSpec.Parse(data) case .Err)
|
|
{
|
|
var err = scope String();
|
|
err.AppendF("Unable to parse version specifier for {0} in {1}", projectName, workspaceFileName);
|
|
Fail(err);
|
|
LoadFailed();
|
|
continue;
|
|
}
|
|
|
|
switch (AddProject(projectName, projSpec.mVerSpec))
|
|
{
|
|
case .Ok(let project):
|
|
if ((!startupProjectName.IsEmpty) && (StringView.Compare(startupProjectName, projectName, true) == 0))
|
|
{
|
|
mWorkspace.mStartupProject = 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);
|
|
}
|
|
|
|
FlushDeferredLoadProjects();
|
|
|
|
mWorkspace.FinishDeserialize(data);
|
|
mWorkspace.FixOptions(mConfigName, mPlatformName);
|
|
#if !CLI
|
|
if (mBfResolveCompiler != null)
|
|
mBfResolveCompiler.QueueSetWorkspaceOptions(null, 0);
|
|
#endif
|
|
|
|
MarkDirty();
|
|
}
|
|
|
|
public void RetryProjectLoad(Project project)
|
|
{
|
|
LoadConfig();
|
|
|
|
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;
|
|
}
|
|
else
|
|
{
|
|
FlushDeferredLoadProjects();
|
|
mWorkspace.FixOptions();
|
|
|
|
project.mFailed = false;
|
|
mProjectPanel.RebuildUI();
|
|
CurrentWorkspaceConfigChanged();
|
|
}
|
|
}
|
|
|
|
public enum ProjectAddError
|
|
{
|
|
InvalidVersion,
|
|
InvalidVersionSpec,
|
|
LoadFailed,
|
|
NotFound
|
|
}
|
|
|
|
public Result<Project, ProjectAddError> AddProject(StringView projectName, VerSpecRecord verSpecRecord)
|
|
{
|
|
VerSpecRecord useVerSpecRecord = verSpecRecord;
|
|
String verConfigDir = mWorkspace.mDir;
|
|
|
|
if (let project = mWorkspace.FindProject(projectName))
|
|
return project;
|
|
|
|
if (useVerSpecRecord.mVerSpec case .SemVer)
|
|
{
|
|
for (int regEntryIdx = mBeefConfig.mRegistry.Count - 1; regEntryIdx >= 0; regEntryIdx--)
|
|
{
|
|
var regEntry = mBeefConfig.mRegistry[regEntryIdx];
|
|
|
|
if (regEntry.mProjName == projectName)
|
|
{
|
|
useVerSpecRecord = regEntry.mLocation;
|
|
verConfigDir = regEntry.mConfigFile.mConfigDir;
|
|
}
|
|
}
|
|
}
|
|
|
|
var project = new Project();
|
|
|
|
// For project locking, assume that only anything that is referenced with a path is editable
|
|
project.mLockedDefault = !(verSpecRecord.mVerSpec case .Path);
|
|
project.mLocked = project.mLockedDefault;
|
|
|
|
mWorkspace.mProjects.Add(project);
|
|
bool success = false;
|
|
defer
|
|
{
|
|
if (!success)
|
|
{
|
|
mWorkspace.mProjects.Remove(project);
|
|
delete project;
|
|
}
|
|
}
|
|
|
|
String projectFilePath = null;
|
|
|
|
switch (useVerSpecRecord.mVerSpec)
|
|
{
|
|
case .Path(let path):
|
|
var relPath = scope String(path);
|
|
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):
|
|
//
|
|
default:
|
|
Fail("Invalid version specifier");
|
|
return .Err(.InvalidVersionSpec);
|
|
}
|
|
|
|
if (projectFilePath == null)
|
|
{
|
|
return .Err(.NotFound);
|
|
}
|
|
|
|
project.mProjectName.Set(projectName);
|
|
project.DeferLoad(projectFilePath);
|
|
success = true;
|
|
mWorkspace.AddProjectToCache(project);
|
|
|
|
/*if (!project.Load(projectFilePath))
|
|
{
|
|
Fail(StackStringFormat!("Failed to load project {0}", projectFilePath));
|
|
delete project;
|
|
return .Err(.LoadFailed);
|
|
}
|
|
|
|
success = true;
|
|
AddProjectToWorkspace(project, false);*/
|
|
return .Ok(project);
|
|
}
|
|
|
|
protected void WorkspaceLoaded()
|
|
{
|
|
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;
|
|
}
|
|
|
|
List<String> platforms = scope List<String>();
|
|
if (IDEApp.sPlatform32Name != null)
|
|
platforms.Add(IDEApp.sPlatform32Name);
|
|
if (IDEApp.sPlatform64Name != null)
|
|
platforms.Add(IDEApp.sPlatform64Name);
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
mWorkspace.FixOptions(mConfigName, mPlatformName);
|
|
#if !CLI
|
|
if (mBfResolveSystem != null)
|
|
PreConfigureBeefSystem(mBfResolveSystem, mBfResolveCompiler);
|
|
#endif
|
|
for (var project in mWorkspace.mProjects)
|
|
{
|
|
project.mEnabled = IsProjectEnabled(project);
|
|
#if !CLI
|
|
if (mBfResolveSystem != null)
|
|
SetupBeefProjectSettings(mBfResolveSystem, mBfResolveCompiler, project);
|
|
#endif
|
|
project.mRootFolder.SortItems();
|
|
}
|
|
|
|
if (mWorkspace.IsSingleFileWorkspace)
|
|
{
|
|
AddToRecentDisplayedFilesList(CompositeFile.sMainFileName);
|
|
}
|
|
}
|
|
|
|
///
|
|
|
|
void DeserializeTabbedView(StructuredData data, IDETabbedView tabbedView)
|
|
{
|
|
if (data.GetBool("DefaultDocumentsTabbedView"))
|
|
mActiveDocumentsTabbedView = tabbedView;
|
|
|
|
SourceViewTab activeTab = null;
|
|
for (data.Enumerate("Tabs"))
|
|
{
|
|
Panel panel = Panel.Create(data);
|
|
if (panel == null)
|
|
continue;
|
|
Debug.Assert(panel != null);
|
|
|
|
bool isActive = data.GetBool("Active");
|
|
|
|
var newTabButton = new SourceViewTab();
|
|
newTabButton.Label = "";
|
|
data.GetString("TabLabel", newTabButton.mLabel);
|
|
newTabButton.mOwnsContent = panel.mAutoDelete;
|
|
newTabButton.mTabWidthOffset = panel.TabWidthOffset;
|
|
//newTabButton.mWantWidth = (float)Math.Round(data.GetFloat("TabWidth") * DarkTheme.sScale);
|
|
newTabButton.mHeight = tabbedView.mTabHeight;
|
|
newTabButton.mContent = panel;
|
|
tabbedView.AddTab(newTabButton, tabbedView.GetTabCount());
|
|
newTabButton.RehupScale(1.0f, 1.0f);
|
|
|
|
newTabButton.mCloseClickedEvent.Add(new () => DocumentCloseClicked(panel));
|
|
|
|
if (isActive)
|
|
activeTab = newTabButton;
|
|
}
|
|
|
|
if (activeTab != null)
|
|
activeTab.Activate(false);
|
|
}
|
|
|
|
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++)
|
|
//for (var dockedWidgetKV in data)
|
|
{
|
|
//using (data.Open(dockedWidgetIdx))
|
|
//using (data.Open(@dockedWidgetKV))
|
|
{
|
|
DockedWidget dockedWidget = null;
|
|
IDETabbedView tabbedView = null;
|
|
|
|
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");
|
|
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;
|
|
|
|
dockingFrame.AddWidget(dockedWidget);
|
|
dockingFrame.mDockedWidgets.Add(dockedWidget);
|
|
}
|
|
}
|
|
}
|
|
dockingFrame.Rehup();
|
|
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");
|
|
if ((width > 0) && (height > 0))
|
|
{
|
|
mRequestedWindowRect = Rect(x, y, width, height);
|
|
}
|
|
mRequestedShowKind = data.GetEnum<BFWindow.ShowKind>("ShowKind");
|
|
}
|
|
|
|
bool LoadWorkspaceUserData(StructuredData data)
|
|
{
|
|
//
|
|
{
|
|
String configName = scope String();
|
|
data.GetString("LastConfig", configName);
|
|
if (!configName.IsEmpty)
|
|
{
|
|
mConfigName.Set(configName);
|
|
}
|
|
|
|
String platformName = scope String();
|
|
data.GetString("LastPlatform", platformName);
|
|
if (!platformName.IsEmpty)
|
|
{
|
|
mPlatformName.Set(platformName);
|
|
}
|
|
}
|
|
|
|
using (data.Open("MainWindow"))
|
|
DeserializeWindow(data, mMainWindow);
|
|
|
|
if (mMainWindow == null)
|
|
mMainFrame.Resize(0, 0, mRequestedWindowRect.mWidth, mRequestedWindowRect.mHeight);
|
|
|
|
using (data.Open("MainDockingFrame"))
|
|
DeserializeDockingFrame(data, mDockingFrame);
|
|
|
|
DeleteAndClearItems!(mRecentlyDisplayedFiles);
|
|
for (data.Enumerate("RecentFilesList"))
|
|
{
|
|
String fileStr = new String();
|
|
data.GetCurString(fileStr);
|
|
IDEUtils.FixFilePath(fileStr);
|
|
mRecentlyDisplayedFiles.Add(fileStr);
|
|
}
|
|
|
|
for (var _breakpoint in data.Enumerate("Breakpoints"))
|
|
{
|
|
String fileName = scope String();
|
|
data.GetString("File", fileName);
|
|
IDEUtils.FixFilePath(fileName);
|
|
int32 lineNum = data.GetInt("Line");
|
|
int32 column = data.GetInt("Column");
|
|
int32 instrOffset = data.GetInt("InstrOffset", -1);
|
|
String memoryWatchExpression = scope String();
|
|
data.GetString("MemoryWatchExpression", memoryWatchExpression);
|
|
Breakpoint breakpoint = null;
|
|
if (memoryWatchExpression.Length > 0)
|
|
breakpoint = mDebugger.CreateMemoryBreakpoint(memoryWatchExpression, (int)0, 0, null);
|
|
else if (fileName.Length > 0)
|
|
breakpoint = mDebugger.CreateBreakpoint(fileName, lineNum, column, instrOffset);
|
|
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);
|
|
}
|
|
}
|
|
|
|
for (var _bookmark in data.Enumerate("Bookmarks"))
|
|
{
|
|
String fileName = scope String();
|
|
data.GetString("File", fileName);
|
|
IDEUtils.FixFilePath(fileName);
|
|
int32 lineNum = data.GetInt("Line");
|
|
int32 column = data.GetInt("Column");
|
|
mBookmarkManager.CreateBookmark(fileName, lineNum, column);
|
|
}
|
|
|
|
for (var referenceId in data.Enumerate("DebuggerDisplayTypes"))
|
|
{
|
|
var referenceIdStr = scope String(referenceId);
|
|
if (referenceIdStr.Length == 0)
|
|
referenceIdStr = null;
|
|
|
|
var intDisplayType = data.GetEnum<DebugManager.IntDisplayType>("IntDisplayType");
|
|
var mmDisplayType = data.GetEnum<DebugManager.MmDisplayType>("MmDisplayType");
|
|
var floatDisplayType = data.GetEnum<DebugManager.FloatDisplayType>("FloatDisplayType");
|
|
mDebugger.SetDisplayTypes(referenceIdStr, intDisplayType, mmDisplayType, floatDisplayType);
|
|
}
|
|
|
|
for (data.Enumerate("StepFilters"))
|
|
{
|
|
String filter = scope String();
|
|
data.GetCurString(filter);
|
|
if (!filter.IsEmpty)
|
|
mDebugger.CreateStepFilter(filter, false, .Filtered);
|
|
}
|
|
|
|
for (data.Enumerate("StepNotFilters"))
|
|
{
|
|
String filter = scope String();
|
|
data.GetCurString(filter);
|
|
if (!filter.IsEmpty)
|
|
mDebugger.CreateStepFilter(filter, false, .NotFiltered);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool LoadWorkspaceUserData()
|
|
{
|
|
scope AutoBeefPerf("IDEApp.LoadWorkspaceUserData");
|
|
//return false;
|
|
|
|
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;
|
|
return true;
|
|
}
|
|
|
|
///
|
|
|
|
public void RehupStepFilters()
|
|
{
|
|
mDebugger.SetStepOverExternalFiles(mStepOverExternalFiles);
|
|
}
|
|
|
|
public void SaveClangFiles()
|
|
{
|
|
WithSourceViewPanels(scope (sourceViewPanel) =>
|
|
{
|
|
if (sourceViewPanel.mIsClang)
|
|
SaveFile(sourceViewPanel);
|
|
});
|
|
}
|
|
|
|
[IDECommand]
|
|
public void SaveFile()
|
|
{
|
|
var sourceViewPanel = GetActiveSourceViewPanel();
|
|
if (sourceViewPanel != null)
|
|
SaveFile(sourceViewPanel);
|
|
}
|
|
|
|
[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 .();
|
|
|
|
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);
|
|
}
|
|
},
|
|
new () =>
|
|
{
|
|
NewDebugSession(false);
|
|
}, null);
|
|
|
|
if (done)
|
|
{
|
|
NewDebugSession(false);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
[IDECommand]
|
|
public void Cmd_NewWorkspace()
|
|
{
|
|
mDeferredOpen = .NewWorkspaceOrProject;
|
|
}
|
|
|
|
[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]
|
|
public bool SaveAll()
|
|
{
|
|
bool success = true;
|
|
|
|
WithSourceViewPanels(scope [&] (sourceViewPanel) =>
|
|
{
|
|
success &= SaveFile(sourceViewPanel);
|
|
});
|
|
mWorkspace.WithProjectItems(scope [&] (projectItem) =>
|
|
{
|
|
var projectSource = projectItem as ProjectSource;
|
|
if (projectSource != null)
|
|
{
|
|
if ((projectSource.mEditData != null) && (projectSource.mEditData.HasTextChanged()))
|
|
{
|
|
success &= SaveFile(projectSource);
|
|
}
|
|
}
|
|
|
|
});
|
|
for (var project in mWorkspace.mProjects)
|
|
{
|
|
if (project.mHasChanged)
|
|
{
|
|
if (project.IsDebugSession)
|
|
success &= SaveDebugSession();
|
|
else
|
|
project.Save();
|
|
}
|
|
}
|
|
if ((mWorkspace.IsInitialized) && (!mWorkspace.IsDebugSession) &&
|
|
((mWorkspace.mHasChanged) || (mWorkspace.mDir == null)))
|
|
success &= SaveWorkspace();
|
|
|
|
if (!mRunningTestScript)
|
|
{
|
|
#if !CLI
|
|
if (!mWorkspace.IsDebugSession)
|
|
success &= SaveWorkspaceUserData();
|
|
if (mSettings.mLoadedSettings)
|
|
mSettings.Save();
|
|
#endif
|
|
}
|
|
|
|
MarkDirty();
|
|
|
|
return success;
|
|
}
|
|
|
|
[IDECommand]
|
|
void Cmd_Exit()
|
|
{
|
|
mMainWindow.Close();
|
|
}
|
|
|
|
WidgetWindow GetCurrentWindow()
|
|
{
|
|
if (mRunningTestScript)
|
|
{
|
|
if (mLastActivePanel != null)
|
|
return mLastActivePanel.mWidgetWindow;
|
|
return mMainWindow;
|
|
}
|
|
|
|
for (var window in mWindows)
|
|
{
|
|
if (window.mHasFocus)
|
|
{
|
|
var returnWindow = window;
|
|
// With this "modal" flag, it caused errors to popup within a hoverwatch on a failed variable edit
|
|
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", " ");
|
|
}
|
|
|
|
// Always write to STDOUT even if we're running as a GUI, allowing cases like RunAndWait to pass us a stdout handle
|
|
Console.Error.WriteLine("ERROR: {0}", text);
|
|
|
|
#if CLI
|
|
mFailed = true;
|
|
return null;
|
|
#endif
|
|
|
|
#unwarn
|
|
if ((mMainWindow == null) || (mShuttingDown))
|
|
{
|
|
mDeferredFails.Add(new String(text));
|
|
return null;
|
|
}
|
|
|
|
|
|
if (mRunningTestScript)
|
|
{
|
|
if (mScriptManager.IsErrorExpected(text))
|
|
{
|
|
DeleteAndNullify!(mScriptManager.mExpectingError);
|
|
OutputLine("Received expected error: {0}", text);
|
|
return null;
|
|
}
|
|
|
|
ShowOutput();
|
|
|
|
mFailed = true;
|
|
OutputLineSmart("ERROR: {0}", text);
|
|
Console.Error.WriteLine("ERROR: {0}", text);
|
|
|
|
return null;
|
|
}
|
|
|
|
#unwarn
|
|
Debug.Assert(Thread.CurrentThread == mMainThread);
|
|
|
|
if (mMainWindow == null)
|
|
{
|
|
Internal.FatalError(StackStringFormat!("FAILED: {0}", text));
|
|
}
|
|
|
|
Beep(MessageBeepType.Error);
|
|
|
|
Dialog dialog = ThemeFactory.mDefault.CreateDialog("ERROR", text, DarkTheme.sDarkTheme.mIconError);
|
|
dialog.mDefaultButton = dialog.AddButton("OK");
|
|
dialog.mEscButton = dialog.mDefaultButton;
|
|
dialog.mWindowFlags |= .Modal;
|
|
dialog.PopupWindow(parentWindow ?? GetCurrentWindow());
|
|
|
|
if (addWidget != null)
|
|
{
|
|
dialog.AddWidget(addWidget);
|
|
addWidget.mY = dialog.mHeight - 60;
|
|
addWidget.mX = 90;
|
|
}
|
|
return dialog;
|
|
}
|
|
|
|
public void MessageDialog(String title, String text)
|
|
{
|
|
Dialog dialog = ThemeFactory.mDefault.CreateDialog(title, text);
|
|
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);
|
|
else
|
|
{
|
|
if (let activeWindow = GetActiveWindow())
|
|
{
|
|
var widget = activeWindow.mFocusWidget;
|
|
while (widget != null)
|
|
{
|
|
if (let watchStringEdit = widget as WatchStringEdit)
|
|
{
|
|
watchStringEdit.ShowQuickFind(isReplace);
|
|
return;
|
|
}
|
|
|
|
widget = widget.mParent;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[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);
|
|
}
|
|
|
|
private void DoFindAndReplace(bool isReplace)
|
|
{
|
|
RecordHistoryLocation();
|
|
|
|
if (mFindAndReplaceDialog != null)
|
|
{
|
|
mFindAndReplaceDialog.mWidgetWindow.SetForeground();
|
|
return;
|
|
}
|
|
|
|
mFindAndReplaceDialog = new FindAndReplaceDialog(isReplace);
|
|
mFindAndReplaceDialog.PopupWindow(mMainWindow);
|
|
mFindAndReplaceDialog.mOnClosed.Add(new () => { mFindAndReplaceDialog = null; });
|
|
}
|
|
|
|
[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);
|
|
}
|
|
|
|
public void DoFindNext(int32 dir = 1)
|
|
{
|
|
var textPanel = GetActiveTextPanel();
|
|
if (textPanel != null)
|
|
{
|
|
textPanel.FindNext(dir);
|
|
}
|
|
else
|
|
{
|
|
if (let activeWindow = GetActiveWindow())
|
|
{
|
|
var widget = activeWindow.mFocusWidget;
|
|
while (widget != null)
|
|
{
|
|
if (let watchStringEdit = widget as WatchStringEdit)
|
|
{
|
|
watchStringEdit.FindNext(dir);
|
|
return;
|
|
}
|
|
|
|
widget = widget.mParent;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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) =>
|
|
{
|
|
if (tabbedView.mIsFillWidget)
|
|
{
|
|
if (firstTabbedView == null)
|
|
firstTabbedView = tabbedView;
|
|
if (tabbedView == mActiveDocumentsTabbedView)
|
|
foundActiveTabbedView = true;
|
|
else if ((foundActiveTabbedView) && (nextTabbedView == null))
|
|
nextTabbedView = tabbedView;
|
|
}
|
|
});
|
|
if (nextTabbedView == null)
|
|
nextTabbedView = firstTabbedView;
|
|
if (nextTabbedView != null)
|
|
{
|
|
if (!nextTabbedView.mWidgetWindow.mHasFocus)
|
|
nextTabbedView.mWidgetWindow.SetForeground();
|
|
var activeTab = nextTabbedView.GetActiveTab();
|
|
activeTab.Activate();
|
|
|
|
if (let sourceViewPanel = activeTab.mContent as SourceViewPanel)
|
|
{
|
|
sourceViewPanel.HilitePosition(.Always);
|
|
}
|
|
}
|
|
}
|
|
|
|
[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]
|
|
public void Cmd_GotoLine()
|
|
{
|
|
var sourceViewPanel = GetActiveSourceViewPanel();
|
|
if (sourceViewPanel != null)
|
|
{
|
|
sourceViewPanel.GotoLine();
|
|
return;
|
|
}
|
|
|
|
var activePanel = GetActivePanel();
|
|
if (let memoryPanel = activePanel as MemoryPanel)
|
|
{
|
|
memoryPanel.GotoAddress();
|
|
}
|
|
}
|
|
|
|
[IDECommand]
|
|
public void Cmd_GotoMethod()
|
|
{
|
|
var sourceViewPanel = GetActiveSourceViewPanel();
|
|
if (sourceViewPanel != null)
|
|
sourceViewPanel.GotoMethod();
|
|
}
|
|
|
|
[IDECommand]
|
|
public void Cmd_RenameItem()
|
|
{
|
|
let activePanel = GetActivePanel();
|
|
if (var projectPanel = activePanel as ProjectPanel)
|
|
{
|
|
projectPanel.TryRenameItem();
|
|
}
|
|
else if (var watchPanel = activePanel as WatchPanel)
|
|
{
|
|
watchPanel.TryRenameItem();
|
|
}
|
|
}
|
|
|
|
[IDECommand]
|
|
public void Cmd_RenameSymbol()
|
|
{
|
|
var sourceViewPanel = GetActiveSourceViewPanel();
|
|
if (sourceViewPanel != null)
|
|
sourceViewPanel.RenameSymbol();
|
|
}
|
|
|
|
[IDECommand]
|
|
public void Cmd_FindAllReferences()
|
|
{
|
|
var sourceViewPanel = GetActiveSourceViewPanel();
|
|
if (sourceViewPanel != null)
|
|
sourceViewPanel.FindAllReferences();
|
|
}
|
|
|
|
[IDECommand]
|
|
public void Cmd_FindClass()
|
|
{
|
|
var widgetWindow = GetCurrentWindow();
|
|
if (widgetWindow != null)
|
|
{
|
|
var dialog = new FindClassDialog();
|
|
dialog.PopupWindow(mMainWindow);
|
|
}
|
|
}
|
|
|
|
[IDECommand]
|
|
public void Cmd_ViewWhiteSpace()
|
|
{
|
|
mViewWhiteSpace.Toggle();
|
|
MarkDirty();
|
|
}
|
|
|
|
[IDECommand]
|
|
public void Cmd_ShowAutoComplete()
|
|
{
|
|
var sewc = GetActiveSourceEditWidgetContent();
|
|
if (sewc != null)
|
|
{
|
|
sewc.ShowAutoComplete(true);
|
|
}
|
|
}
|
|
|
|
[IDECommand]
|
|
public void Cmd_ShowFixit()
|
|
{
|
|
var sourceViewPanel = GetActiveSourceViewPanel();
|
|
if (sourceViewPanel != null)
|
|
sourceViewPanel.FixitAtCursor();
|
|
}
|
|
|
|
[IDECommand]
|
|
public void Cmd_PrevBookmark()
|
|
{
|
|
mBookmarkManager.PrevBookmark();
|
|
}
|
|
|
|
[IDECommand]
|
|
public void Cmd_NextBookmark()
|
|
{
|
|
mBookmarkManager.NextBookmark();
|
|
}
|
|
|
|
[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)
|
|
{
|
|
Fail("Cannot clean while compiling");
|
|
return; // Ignore
|
|
}
|
|
if (mDebugger.mIsRunning)
|
|
{
|
|
Fail("Cannot clean while running");
|
|
return; // Ignore
|
|
}
|
|
mWantsClean = true;
|
|
}
|
|
|
|
[IDECommand]
|
|
public void Cmd_CleanBeef()
|
|
{
|
|
if (IsCompiling)
|
|
{
|
|
Fail("Cannot clean while compiling");
|
|
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)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
[IDECommand]
|
|
public void Cmd_MatchBrace()
|
|
{
|
|
var sourceViewPanel = GetActiveSourceViewPanel();
|
|
if (sourceViewPanel != null)
|
|
sourceViewPanel.MatchBrace();
|
|
}
|
|
|
|
[IDECommand]
|
|
public void Cmd_GotoNextItem()
|
|
{
|
|
var curOutputPanel = mOutputPanel;
|
|
if ((mFindResultsPanel != null) && (mFindResultsPanel.mWidgetWindow != null))
|
|
{
|
|
if (mFindResultsPanel.mLastFocusAppUpdateCnt > curOutputPanel.mLastFocusAppUpdateCnt)
|
|
curOutputPanel = mFindResultsPanel;
|
|
}
|
|
curOutputPanel.GotoNextSourceReference();
|
|
}
|
|
|
|
[IDECommand]
|
|
public void Cmd_ZoomOut()
|
|
{
|
|
float scale = DarkTheme.sScale;
|
|
if (scale > 0.25f)
|
|
{
|
|
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)
|
|
scale += 0.02f;//0.05f;
|
|
else //if (scale < 2.0f)
|
|
scale += 0.04f;//0.10f;
|
|
|
|
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);
|
|
}
|
|
|
|
[IDECommand]
|
|
public void Cmd_QuickInfo()
|
|
{
|
|
var sourceViewPanel = GetActiveSourceViewPanel(true);
|
|
if (sourceViewPanel != null)
|
|
{
|
|
if (sourceViewPanel.mEditWidget.mEditWidgetContent.GetCursorLineChar(var line, var lineChar))
|
|
sourceViewPanel.UpdateMouseover(true, true, line, lineChar);
|
|
}
|
|
}
|
|
|
|
[IDECommand]
|
|
public void Cmd_ReformatDocument()
|
|
{
|
|
var sourceViewPanel = GetActiveSourceViewPanel(true);
|
|
if (sourceViewPanel != null)
|
|
sourceViewPanel.ReformatDocument();
|
|
}
|
|
|
|
void RemoveAllBreakpoints()
|
|
{
|
|
BfLog.LogDbg("IDEApp.RemoveAllBreakpoints\n");
|
|
while (mDebugger.mBreakpointList.Count > 0)
|
|
mDebugger.DeleteBreakpoint(mDebugger.mBreakpointList[0]);
|
|
}
|
|
|
|
[IDECommand]
|
|
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
|
|
}
|
|
}
|
|
|
|
[IDECommand]
|
|
public void ShowDisassemblyAtStack()
|
|
{
|
|
var activePanel = GetActivePanel();
|
|
if (var diassemblyPanel = activePanel as DisassemblyPanel)
|
|
{
|
|
diassemblyPanel.mStayInDisassemblyCheckbox.Checked ^= true;
|
|
return;
|
|
}
|
|
|
|
if ((mDebugger.mIsRunning) && (mDebugger.IsPaused()))
|
|
{
|
|
ShowPCLocation(mDebugger.mActiveCallStackIdx, false, false, true);
|
|
}
|
|
else
|
|
{
|
|
ShowDisassemblyPanel(true);
|
|
}
|
|
mInDisassemblyView = true;
|
|
}
|
|
|
|
public void GoToDefinition(bool force)
|
|
{
|
|
var sourceViewPanel = GetActiveSourceViewPanel();
|
|
if (sourceViewPanel != null)
|
|
{
|
|
if (!force)
|
|
{
|
|
if ((!sourceViewPanel.mIsBeefSource) || (sourceViewPanel.mProjectSource == null))
|
|
return;
|
|
}
|
|
|
|
if ((!sourceViewPanel.mEditWidget.Content.GetCursorLineChar(var line, var lineChar)) && (!force))
|
|
return;
|
|
|
|
if (!sourceViewPanel.HasTextAtCursor())
|
|
return;
|
|
|
|
#if IDE_C_SUPPORT
|
|
if (sourceViewPanel.mIsClang)
|
|
{
|
|
String defFile = scope String();
|
|
int defLine;
|
|
int defColumn;
|
|
mResolveClang.CancelBackground();
|
|
|
|
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
|
|
#endif
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (mBfResolveCompiler.HasResolvedAll())
|
|
{
|
|
Fail("Unable to locate definition");
|
|
}
|
|
else
|
|
{
|
|
sourceViewPanel.ShowSymbolReferenceHelper(.GoToDefinition);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void StackPositionChanged()
|
|
{
|
|
WithWatchPanels(scope (watchPanel) =>
|
|
{
|
|
watchPanel.MarkWatchesDirty(false, true);
|
|
});
|
|
mMemoryPanel.MarkViewDirty();
|
|
}
|
|
|
|
public void RefreshWatches()
|
|
{
|
|
//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();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void MemoryEdited()
|
|
{
|
|
RefreshWatches();
|
|
mMemoryPanel.MarkViewDirty();
|
|
}
|
|
|
|
public void AddWatch(String watchExpr)
|
|
{
|
|
ShowWatches();
|
|
mWatchPanel.AddWatchItem(watchExpr);
|
|
mWatchPanel.MarkWatchesDirty(false);
|
|
}
|
|
|
|
public bool IsInDisassemblyMode(bool wantShowSource = false)
|
|
{
|
|
//return GetActiveDocumentPanel() is DisassemblyPanel;
|
|
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;
|
|
}
|
|
|
|
[IDECommand]
|
|
void Compile()
|
|
{
|
|
CompilerLog("IDEApp.Compile");
|
|
|
|
for (let project in gApp.mWorkspace.mProjects)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (AreTestsRunning())
|
|
return;
|
|
if (mHotResolveState != .None)
|
|
return;
|
|
if (IsCompiling)
|
|
return;
|
|
|
|
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))
|
|
{
|
|
if (mExecutionQueue.Count == 0)
|
|
{
|
|
mOutputPanel.Clear();
|
|
OutputLine("Compiling...");
|
|
Compile(.Normal, null);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mOutputPanel.Clear();
|
|
OutputLine("Hot Compiling...");
|
|
Project runningProject = mWorkspace.mStartupProject;
|
|
Compile(.Normal, runningProject);
|
|
}
|
|
}
|
|
|
|
[IDECommand]
|
|
void RunWithStep()
|
|
{
|
|
mTargetStartWithStep = true;
|
|
CompileAndRun(true);
|
|
}
|
|
|
|
[IDECommand]
|
|
void StepInto()
|
|
{
|
|
if (mDebugger.mIsRunning)
|
|
{
|
|
if (mExecutionPaused)
|
|
{
|
|
DebuggerUnpaused();
|
|
mDebugger.StepInto(IsInDisassemblyMode());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RunWithStep();
|
|
}
|
|
}
|
|
|
|
[IDECommand]
|
|
void StepOver()
|
|
{
|
|
mStepCount++;
|
|
if (mDebugger.mIsRunning)
|
|
{
|
|
if (mExecutionPaused)
|
|
{
|
|
DebuggerUnpaused();
|
|
mDebugger.StepOver(IsInDisassemblyMode());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (mEnableRunTiming)
|
|
{
|
|
mRunTimingProfileId = Profiler.StartSampling("RunTiming");
|
|
}
|
|
|
|
RunWithStep();
|
|
}
|
|
}
|
|
|
|
[IDECommand]
|
|
void StepOut()
|
|
{
|
|
if (mExecutionPaused)
|
|
{
|
|
DebuggerUnpaused();
|
|
mDebugger.StepOut(IsInDisassemblyMode());
|
|
}
|
|
}
|
|
|
|
[IDECommand]
|
|
void Cmd_Continue()
|
|
{
|
|
if (mDebugger.mIsRunning)
|
|
{
|
|
if (mDebugger.IsPaused())
|
|
{
|
|
DebuggerUnpaused();
|
|
mDebugger.Continue();
|
|
}
|
|
}
|
|
}
|
|
|
|
[IDECommand]
|
|
public void RunWithCompiling()
|
|
{
|
|
if (mDebugger.mIsRunning)
|
|
{
|
|
if (mDebugger.IsPaused())
|
|
{
|
|
DebuggerUnpaused();
|
|
mDebugger.Continue();
|
|
}
|
|
}
|
|
else if (AreTestsRunning())
|
|
{
|
|
// Ignore
|
|
}
|
|
else
|
|
{
|
|
mTargetStartWithStep = false;
|
|
CompileAndRun(true);
|
|
}
|
|
}
|
|
|
|
[IDECommand]
|
|
void RunWithoutCompiling()
|
|
{
|
|
if (!mDebugger.mIsRunning)
|
|
{
|
|
OutputLine("Starting target without compiling...");
|
|
mTargetStartWithStep = false;
|
|
var startDebugCmd = new StartDebugCmd();
|
|
startDebugCmd.mWasCompiled = false;
|
|
startDebugCmd.mOnlyIfNotFailed = true;
|
|
mExecutionQueue.Add(startDebugCmd);
|
|
}
|
|
}
|
|
|
|
[IDECommand]
|
|
void RunWithoutDebugging()
|
|
{
|
|
if (mDebugger.mIsRunning)
|
|
{
|
|
if (mDebugger.IsPaused())
|
|
{
|
|
DebuggerUnpaused();
|
|
mDebugger.Continue();
|
|
}
|
|
}
|
|
else if (AreTestsRunning())
|
|
{
|
|
// Ignore
|
|
}
|
|
else
|
|
{
|
|
mTargetStartWithStep = false;
|
|
CompileAndRun(false);
|
|
}
|
|
}
|
|
|
|
[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");
|
|
mDebugger.mRunToCursorBreakpoint = sourceViewPanel.ToggleBreakpointAtCursor(.Force, .None, mDebugger.GetActiveThread());
|
|
}
|
|
else if (var disassemblyPanel = GetActiveDocumentPanel() as DisassemblyPanel)
|
|
{
|
|
mDebugger.mRunToCursorBreakpoint = disassemblyPanel.ToggleAddrBreakpointAtCursor(.Force, .None, mDebugger.GetActiveThread());
|
|
}
|
|
|
|
if (mDebugger.mIsRunning)
|
|
{
|
|
if (mDebugger.IsPaused())
|
|
{
|
|
DebuggerUnpaused();
|
|
mDebugger.Continue();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mTargetStartWithStep = false;
|
|
CompileAndRun(true);
|
|
}
|
|
}
|
|
|
|
[IDECommand]
|
|
void SetNextStatement()
|
|
{
|
|
var documentPanel = GetActiveDocumentPanel();
|
|
var sourceViewPanel = documentPanel as SourceViewPanel;
|
|
var disassemblyPanel = GetActiveDocumentPanel() as DisassemblyPanel;
|
|
|
|
if (mDebugger.mIsRunning)
|
|
{
|
|
if (mExecutionPaused)
|
|
{
|
|
if (gApp.mDebugger.mActiveCallStackIdx != 0)
|
|
{
|
|
gApp.Fail("Set Next Statement cannot only be used when the top of the callstack is selected");
|
|
return;
|
|
}
|
|
|
|
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
|
|
{
|
|
var activePanel = sourceViewPanel.GetActivePanel();
|
|
|
|
int lineIdx;
|
|
int lineCharIdx;
|
|
var editWidgetContent = activePanel.mEditWidget.Content;
|
|
editWidgetContent.GetLineCharAtIdx(editWidgetContent.CursorTextPos, out lineIdx, out lineCharIdx);
|
|
|
|
/*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))
|
|
{
|
|
textPos++;
|
|
lineCharIdx++;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
mDebugger.SetNextStatement(false, sourceViewPanel.mFilePath, (int)lineIdx, lineCharIdx);
|
|
|
|
PCChanged();
|
|
DebuggerUnpaused();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ToggleBreakpoint(WidgetWindow window, Breakpoint.SetKind setKind, Breakpoint.SetFlags setFlags, bool bindToThread = false)
|
|
{
|
|
var documentPanel = GetActiveDocumentPanel();
|
|
|
|
if (var sourceViewPanel = documentPanel as SourceViewPanel)
|
|
{
|
|
sourceViewPanel.ToggleBreakpointAtCursor(setKind, setFlags, bindToThread ? gApp.mDebugger.GetActiveThread() : -1);
|
|
}
|
|
else if (var disassemblyPanel = documentPanel as DisassemblyPanel)
|
|
{
|
|
disassemblyPanel.ToggleBreakpointAtCursor(setKind, setFlags, bindToThread ? gApp.mDebugger.GetActiveThread() : -1);
|
|
}
|
|
}
|
|
|
|
[IDECommand]
|
|
void ToggleComment()
|
|
{
|
|
var sourceViewPanel = GetActiveSourceViewPanel();
|
|
if (sourceViewPanel == null)
|
|
return;
|
|
|
|
var ewc = (SourceEditWidgetContent)sourceViewPanel.mEditWidget.mEditWidgetContent;
|
|
ewc.ToggleComment();
|
|
}
|
|
|
|
[IDECommand]
|
|
void ToggleBreakpoint()
|
|
{
|
|
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);
|
|
}
|
|
|
|
[IDECommand]
|
|
void ToggleThreadBreakpoint()
|
|
{
|
|
ToggleBreakpoint(GetCurrentWindow(), .Toggle, .None, true);
|
|
}
|
|
|
|
void CompileCurrentFile()
|
|
{
|
|
var sourceViewPanel = GetActiveSourceViewPanel();
|
|
if (sourceViewPanel == null)
|
|
return;
|
|
|
|
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]
|
|
void CancelBuild()
|
|
{
|
|
mBfBuildCompiler.RequestCancelBackground();
|
|
if (IsCompiling)
|
|
{
|
|
OutputLine("Canceling Compilation...");
|
|
//DeleteAndClearItems!(mExecutionQueue);
|
|
for (var cmd in mExecutionQueue)
|
|
{
|
|
#unwarn
|
|
if (var processBfCompileCmd = cmd as ProcessBfCompileCmd)
|
|
{
|
|
|
|
}
|
|
else if (var buildCompleteCmd = cmd as BuildCompletedCmd)
|
|
{
|
|
|
|
}
|
|
else
|
|
{
|
|
delete cmd;
|
|
@cmd.Remove();
|
|
}
|
|
}
|
|
|
|
for (var executionInstance in mExecutionInstances)
|
|
{
|
|
executionInstance.Cancel();
|
|
}
|
|
}
|
|
|
|
if ((mBuildContext != null) && (mBuildContext.mScriptManager != null))
|
|
mBuildContext.mScriptManager.Cancel();
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
enum ShowTabResult
|
|
{
|
|
Existing,
|
|
OpenedNew
|
|
}
|
|
|
|
ShowTabResult ShowTab(Widget tabContent, String name, bool ownsContent, bool setFocus)
|
|
{
|
|
var result = ShowTabResult.Existing;
|
|
var tabButton = GetTab(tabContent);
|
|
if (tabButton == null)
|
|
{
|
|
TabbedView tabbedView = null;
|
|
if (var newPanel = tabContent as Panel)
|
|
{
|
|
WithTabs(scope [&] (tabButton) =>
|
|
{
|
|
if (newPanel.HasAffinity(tabButton.mContent))
|
|
{
|
|
tabbedView = tabButton.mTabbedView;
|
|
}
|
|
});
|
|
}
|
|
|
|
if (tabbedView == null)
|
|
tabbedView = FindTabbedView(mDockingFrame, -1, 1);
|
|
if (tabbedView == null)
|
|
{
|
|
tabbedView = CreateTabbedView();
|
|
mDockingFrame.AddDockedWidget(tabbedView, null, .Left);
|
|
}
|
|
if (tabbedView != null)
|
|
{
|
|
tabButton = SetupTab(tabbedView, name, 100, tabContent, ownsContent);
|
|
result = ShowTabResult.OpenedNew;
|
|
}
|
|
}
|
|
if (tabButton != null)
|
|
{
|
|
tabButton.RehupScale(1.0f, 1.0f);
|
|
tabButton.Activate(setFocus);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public void RecordHistoryLocation(bool includeLastActive = false)
|
|
{
|
|
var sourceViewPanel = GetActiveSourceViewPanel(includeLastActive);
|
|
if (sourceViewPanel != null)
|
|
sourceViewPanel.RecordHistoryLocation();
|
|
}
|
|
|
|
void ShowPanel(Panel panel, String label, bool setFocus = true)
|
|
{
|
|
if (!mInitialized)
|
|
return;
|
|
#if !CLI
|
|
if (setFocus)
|
|
mLastActivePanel = panel;
|
|
RecordHistoryLocation();
|
|
ShowTab(panel, label, false, setFocus);
|
|
if (setFocus)
|
|
panel.FocusForKeyboard();
|
|
if ((!panel.mWidgetWindow.mHasFocus) && (!mRunningTestScript))
|
|
panel.mWidgetWindow.SetForeground();
|
|
#endif
|
|
}
|
|
|
|
[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()
|
|
{
|
|
ShowPanel(mCallStackPanel, "Call Stack");
|
|
}
|
|
|
|
[IDECommand]
|
|
public void ShowErrors()
|
|
{
|
|
ShowPanel(mErrorsPanel, "Errors");
|
|
}
|
|
|
|
[IDECommand]
|
|
public void ShowErrorNext()
|
|
{
|
|
mErrorsPanel.ShowErrorNext();
|
|
}
|
|
|
|
[IDECommand]
|
|
public void ShowWatches()
|
|
{
|
|
ShowPanel(mWatchPanel, "Watch");
|
|
}
|
|
|
|
[IDECommand]
|
|
public void ShowAutoWatches()
|
|
{
|
|
ShowPanel(mAutoWatchPanel, "Auto Watches");
|
|
}
|
|
|
|
[IDECommand]
|
|
public void ShowImmediatePanel()
|
|
{
|
|
ShowPanel(mImmediatePanel, "Immediate");
|
|
}
|
|
|
|
[IDECommand]
|
|
public void ShowBreakpoints()
|
|
{
|
|
ShowPanel(mBreakpointPanel, "Breakpoints");
|
|
}
|
|
|
|
[IDECommand]
|
|
public void ShowModules()
|
|
{
|
|
ShowPanel(mModulePanel, "Modules");
|
|
}
|
|
|
|
[IDECommand]
|
|
public void ShowMemory()
|
|
{
|
|
ShowPanel(mMemoryPanel, "Memory");
|
|
}
|
|
|
|
[IDECommand]
|
|
public void ShowProfilePanel()
|
|
{
|
|
ShowPanel(mProfilePanel, "Profile");
|
|
}
|
|
|
|
[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
|
|
sourceViewPanel.GetDebugExpressionAt(ewc.CursorTextPos, debugExpr);
|
|
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()
|
|
{
|
|
mMainFrame.mStatusBar.mConfigComboBox.ShowDropdown();
|
|
}
|
|
|
|
[IDECommand]
|
|
public void SelectPlatform()
|
|
{
|
|
mMainFrame.mStatusBar.mPlatformComboBox.ShowDropdown();
|
|
}
|
|
|
|
[IDECommand]
|
|
public void ShowSettings()
|
|
{
|
|
var workspaceProperties = new SettingsDialog();
|
|
workspaceProperties.PopupWindow(mMainWindow);
|
|
}
|
|
|
|
[IDECommand]
|
|
public void ShowKeyboardShortcuts()
|
|
{
|
|
/*var workspaceProperties = new SettingsDialog();
|
|
workspaceProperties.PopupWindow(mMainWindow);*/
|
|
}
|
|
|
|
public void ShowFindResults(bool setFocus)
|
|
{
|
|
ShowPanel(mFindResultsPanel, "Find Results", setFocus);
|
|
}
|
|
|
|
[IDECommand]
|
|
public void ShowFindResults()
|
|
{
|
|
ShowFindResults(true);
|
|
}
|
|
|
|
[IDECommand]
|
|
public void ShowOutput()
|
|
{
|
|
ShowPanel(mOutputPanel, "Output", false);
|
|
}
|
|
|
|
[IDECommand]
|
|
public void ShowAutoCompletePanel()
|
|
{
|
|
ShowPanel(mAutoCompletePanel, "Autocomplete", false);
|
|
}
|
|
|
|
[IDECommand]
|
|
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");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DarkTabbedView GetActiveTabbedView()
|
|
{
|
|
var activePanel = GetActivePanel();
|
|
if (activePanel == null)
|
|
return null;
|
|
return activePanel.mParent as DarkTabbedView;
|
|
}
|
|
|
|
[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;
|
|
TabbedView.TabButton activateTab = tabbedView.mTabs[0];
|
|
for (var tab in tabbedView.mTabs)
|
|
{
|
|
if (tab.mIsActive)
|
|
{
|
|
if (@tab.Index < tabbedView.mTabs.Count - 1)
|
|
{
|
|
activateTab = tabbedView.mTabs[@tab.Index + 1];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
activateTab.Activate();
|
|
if (var sourceViewPanel = activateTab.mContent as SourceViewPanel)
|
|
sourceViewPanel.HilitePosition(.Extra);
|
|
}
|
|
|
|
[IDECommand]
|
|
void TabPrev()
|
|
{
|
|
var tabbedView = GetActiveTabbedView();
|
|
if ((tabbedView == null) || (tabbedView.mTabs.IsEmpty))
|
|
return;
|
|
TabbedView.TabButton activateTab = tabbedView.mTabs.Back;
|
|
for (var tab in tabbedView.mTabs)
|
|
{
|
|
if (tab.mIsActive)
|
|
{
|
|
if (@tab.Index > 0)
|
|
{
|
|
activateTab = tabbedView.mTabs[@tab.Index - 1];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
activateTab.Activate();
|
|
if (var sourceViewPanel = activateTab.mContent as SourceViewPanel)
|
|
sourceViewPanel.HilitePosition(.Extra);
|
|
}
|
|
|
|
void DoErrorTest()
|
|
{
|
|
Dialog aDialog = ThemeFactory.mDefault.CreateDialog("ERROR", "This\nmultiline!\nLine 3.", DarkTheme.sDarkTheme.mIconError);
|
|
aDialog.mDefaultButton = aDialog.AddButton("OK");
|
|
aDialog.mEscButton = aDialog.mDefaultButton;
|
|
aDialog.PopupWindow(GetCurrentWindow());
|
|
}
|
|
|
|
void ReportMemory()
|
|
{
|
|
mDebugger.FullReportMemory();
|
|
if (mBfResolveSystem != null)
|
|
mBfResolveSystem.ReportMemory();
|
|
mBfBuildSystem.ReportMemory();
|
|
Internal.ReportMemory();
|
|
GC.Report();
|
|
}
|
|
|
|
[IDECommand]
|
|
void DoAttach()
|
|
{
|
|
var widgetWindow = GetCurrentWindow();
|
|
if (widgetWindow != null)
|
|
{
|
|
var attachDialog = new AttachDialog();
|
|
attachDialog.PopupWindow(mMainWindow);
|
|
}
|
|
}
|
|
|
|
[IDECommand]
|
|
void DoLaunch()
|
|
{
|
|
if (mLaunchDialog != null)
|
|
{
|
|
mLaunchDialog.mWidgetWindow.SetForeground();
|
|
return;
|
|
}
|
|
|
|
mLaunchDialog = new LaunchDialog();
|
|
mLaunchDialog.PopupWindow(mMainWindow);
|
|
mLaunchDialog.mOnClosed.Add(new () => { mLaunchDialog = null; });
|
|
}
|
|
|
|
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()))
|
|
{
|
|
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();
|
|
}
|
|
|
|
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 bool AreTestsRunning()
|
|
{
|
|
return (mTestManager != null);
|
|
}
|
|
|
|
protected void RunTests(bool includeIgnored, bool debug)
|
|
{
|
|
if (mOutputPanel != null)
|
|
{
|
|
ShowPanel(mOutputPanel, false);
|
|
mOutputPanel.Clear();
|
|
}
|
|
|
|
if (AreTestsRunning())
|
|
{
|
|
OutputErrorLine("Tests already running");
|
|
return;
|
|
}
|
|
|
|
if ((mDebugger != null) && (mDebugger.mIsRunning))
|
|
{
|
|
OutputErrorLine("Tests cannot be run while program is executing");
|
|
return;
|
|
}
|
|
|
|
String prevConfigName = scope String(mConfigName);
|
|
|
|
var workspaceOptions = GetCurWorkspaceOptions();
|
|
if (workspaceOptions.mBuildKind != .Test)
|
|
{
|
|
mMainFrame.mStatusBar.SelectConfig("Test");
|
|
}
|
|
|
|
workspaceOptions = GetCurWorkspaceOptions();
|
|
if (workspaceOptions.mBuildKind != .Test)
|
|
{
|
|
mMainFrame.mStatusBar.SelectConfig(prevConfigName);
|
|
OutputErrorLine("No valid Test workspace configuration exists");
|
|
return;
|
|
}
|
|
|
|
mLastTestFailed = false;
|
|
mTestManager = new TestManager();
|
|
mTestManager.mPrevConfigName = new String(prevConfigName);
|
|
mTestManager.mDebug = debug;
|
|
mTestManager.mIncludeIgnored = includeIgnored;
|
|
|
|
if (mOutputPanel != null)
|
|
mOutputPanel.Clear();
|
|
OutputLine("Compiling for testing...");
|
|
if (!Compile(.Test, null))
|
|
{
|
|
mTestManager.BuildFailed();
|
|
}
|
|
if (!mTestManager.HasProjects)
|
|
{
|
|
OutputLineSmart("WARNING: No projects have a test configuration specified");
|
|
}
|
|
}
|
|
|
|
[IDECommand]
|
|
public void Cmd_TestEnableConsole()
|
|
{
|
|
let ideCommand = gApp.mCommands.mCommandMap["Test Enable Console"];
|
|
ToggleCheck(ideCommand.mMenuItem, ref mTestEnableConsole);
|
|
}
|
|
|
|
public void UpdateMenuItem_HasActivePanel(IMenu menu)
|
|
{
|
|
menu.SetDisabled(GetActivePanel() == null);
|
|
}
|
|
|
|
public void UpdateMenuItem_HasActiveDocument(IMenu menu)
|
|
{
|
|
menu.SetDisabled(GetActiveDocumentPanel() == null);
|
|
}
|
|
|
|
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_DebugStopped_HasWorkspace(IMenu menu)
|
|
{
|
|
menu.SetDisabled(mDebugger.mIsRunning || !mWorkspace.IsInitialized);
|
|
}
|
|
|
|
public void UpdateMenuItem_DebugStopped(IMenu menu)
|
|
{
|
|
menu.SetDisabled(mDebugger.mIsRunning);
|
|
}
|
|
|
|
public void CreateMenu()
|
|
{
|
|
scope AutoBeefPerf("IDEApp.CreateMenu");
|
|
|
|
SysMenu root = mMainWindow.mSysMenu;
|
|
|
|
String keyStr = scope String();
|
|
|
|
SysMenu AddMenuItem(SysMenu menu, String dispString, String cmdName, MenuItemUpdateHandler menuItemUpdateHandler = null,
|
|
SysBitmap bitmap = null, bool enabled = true, int32 checkState = -1, bool radioCheck = false)
|
|
{
|
|
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;
|
|
}
|
|
|
|
//////////
|
|
|
|
SysMenu subMenu = root.AddMenuItem("&File");
|
|
let newMenu = subMenu.AddMenuItem("&New");
|
|
AddMenuItem(newMenu, "New &Workspace", "New Workspace");
|
|
AddMenuItem(newMenu, "New &Project", "New Project");
|
|
AddMenuItem(newMenu, "New &Debug Session", "New Debug Session");
|
|
AddMenuItem(newMenu, "New &File", "New File");
|
|
|
|
let openMenu = subMenu.AddMenuItem("&Open");
|
|
//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");
|
|
|
|
AddMenuItem(subMenu, "&Save File","Save File", new => UpdateMenuItem_HasActiveDocument);
|
|
AddMenuItem(subMenu, "Save &As...", "Save As", new => UpdateMenuItem_HasActiveDocument);
|
|
AddMenuItem(subMenu, "Save A&ll", "Save All");
|
|
let prefMenu = subMenu.AddMenuItem("&Preferences");
|
|
//prefMenu.AddMenuItem("&Keyboard Shortcuts", null, new (evt) => { ShowKeyboardShortcuts(); });
|
|
AddMenuItem(prefMenu, "&Settings", "Settings");
|
|
AddMenuItem(subMenu, "Close Workspace", "Close Workspace", new => UpdateMenuItem_HasWorkspace);
|
|
AddMenuItem(subMenu, "E&xit", "Exit");
|
|
|
|
//////////
|
|
|
|
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");
|
|
AddMenuItem(subMenu, "Find Prev", "Find Prev", new => UpdateMenuItem_HasActivePanel);
|
|
AddMenuItem(subMenu, "Find Next", "Find Next", new => UpdateMenuItem_HasActivePanel);
|
|
AddMenuItem(subMenu, "Show &Current", "Show Current");
|
|
|
|
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);
|
|
AddMenuItem(subMenu, "Show Fi&xit", "Show Fixit", new => UpdateMenuItem_HasActiveDocument);
|
|
AddMenuItem(subMenu, "Find &All References", "Find All References", new => UpdateMenuItem_HasActiveDocument);
|
|
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 advancedEditMenu = subMenu.AddMenuItem("Advanced");
|
|
AddMenuItem(advancedEditMenu, "Make Uppercase", "Make Uppercase");
|
|
AddMenuItem(advancedEditMenu, "Make Lowercase", "Make Lowercase");
|
|
mViewWhiteSpace.mMenu = AddMenuItem(advancedEditMenu, "View White Space", "View White Space", null, null, true, mViewWhiteSpace.Bool ? 1 : 0);
|
|
AddMenuItem(advancedEditMenu, "Reformat Document", "Reformat Document");
|
|
|
|
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);
|
|
}
|
|
|
|
//////////
|
|
|
|
subMenu = root.AddMenuItem("&View");
|
|
AddMenuItem(subMenu, "A&utoComplete", "Show Autocomplete Panel");
|
|
AddMenuItem(subMenu, "&Auto Watches", "Show Auto Watches");
|
|
AddMenuItem(subMenu, "&Breakpoints", "Show Breakpoints");
|
|
AddMenuItem(subMenu, "&Call Stack", "Show Call Stack");
|
|
AddMenuItem(subMenu, "C&lass View", "Show Class View");
|
|
AddMenuItem(subMenu, "E&rrors", "Show Errors");
|
|
AddMenuItem(subMenu, "&Find Results", "Show Find Results");
|
|
AddMenuItem(subMenu, "&Immediate Window", "Show Immediate");
|
|
AddMenuItem(subMenu, "&Memory", "Show Memory");
|
|
AddMenuItem(subMenu, "Mo&dules", "Show Modules");
|
|
AddMenuItem(subMenu, "&Output", "Show Output");
|
|
AddMenuItem(subMenu, "&Profiler", "Show Profiler");
|
|
AddMenuItem(subMenu, "&Threads", "Show Threads");
|
|
AddMenuItem(subMenu, "&Watches", "Show Watches");
|
|
AddMenuItem(subMenu, "Work&space Explorer", "Show Workspace Explorer");
|
|
subMenu.AddMenuItem(null);
|
|
AddMenuItem(subMenu, "Next Document Panel", "Next Document Panel");
|
|
AddMenuItem(subMenu, "Navigate Backwards", "Navigate Backwards");
|
|
AddMenuItem(subMenu, "Navigate Forwards", "Navigate Forwards");
|
|
|
|
//////////
|
|
|
|
subMenu = root.AddMenuItem("&Build");
|
|
AddMenuItem(subMenu, "&Build Solution", "Build Solution", new => UpdateMenuItem_HasWorkspace);
|
|
AddMenuItem(subMenu, "&Clean", "Clean", new => UpdateMenuItem_DebugStopped_HasWorkspace);
|
|
AddMenuItem(subMenu, "Clean Beef", "Clean Beef", new => UpdateMenuItem_DebugStopped_HasWorkspace);
|
|
//subMenu.AddMenuItem("Compile Current File", null, new (menu) => { CompileCurrentFile(); });
|
|
AddMenuItem(subMenu, "Cancel Build", "Cancel Build", new (menu) => { menu.SetDisabled(!IsCompiling); });
|
|
|
|
if (mSettings.mEnableDevMode)
|
|
{
|
|
var internalBuildMenu = subMenu.AddMenuItem("Internal");
|
|
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);
|
|
}
|
|
|
|
//////////
|
|
|
|
subMenu = root.AddMenuItem("&Debug");
|
|
AddMenuItem(subMenu, "&Start Debugging", "Start Debugging", new (item) =>
|
|
{
|
|
SysMenu sysMenu = (.)item;
|
|
if (mDebugger.mIsRunning)
|
|
sysMenu.Modify("&Continue", sysMenu.mHotKey, null, mDebugger.IsPaused());
|
|
else
|
|
sysMenu.Modify("&Start Debugging", sysMenu.mHotKey, null, mWorkspace.IsInitialized);
|
|
|
|
});
|
|
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);
|
|
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_DebugRunning);
|
|
AddMenuItem(subMenu, "Break All", "Break All", new => UpdateMenuItem_DebugNotPaused);
|
|
AddMenuItem(subMenu, "Remove All Breakpoints", "Remove All Breakpoints");
|
|
AddMenuItem(subMenu, "Show &Disassembly", "Show Disassembly");
|
|
AddMenuItem(subMenu, "&Quick Watch", "Show QuickWatch", new => UpdateMenuItem_DebugPaused);
|
|
AddMenuItem(subMenu, "&Profile", "Profile", new => UpdateMenuItem_HasWorkspace);
|
|
subMenu.AddMenuItem(null);
|
|
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);
|
|
subMenu.AddMenuItem(null);
|
|
AddMenuItem(subMenu, "To&ggle Breakpoint", "Breakpoint Toggle", new => UpdateMenuItem_HasActiveDocument);
|
|
AddMenuItem(subMenu, "Toggle Thread Breakpoint", "Breakpoint Toggle Thread", new => UpdateMenuItem_HasActiveDocument);
|
|
var newBreakpointMenu = subMenu.AddMenuItem("New &Breakpoint");
|
|
AddMenuItem(newBreakpointMenu, "&Memory Breakpoint...", "Breakpoint Memory", new => UpdateMenuItem_DebugRunning);
|
|
AddMenuItem(newBreakpointMenu, "&Symbol Breakpoint...", "Breakpoint Symbol");
|
|
|
|
if (mSettings.mEnableDevMode)
|
|
{
|
|
var internalDebugMenu = subMenu.AddMenuItem("Internal");
|
|
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) => { Runtime.FatalError("Bad"); });
|
|
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) =>
|
|
{
|
|
var profileId = Profiler.StartSampling().GetValueOrDefault();
|
|
for (int i < 10)
|
|
GC.Collect(false);
|
|
if (profileId != 0)
|
|
profileId.Dispose();
|
|
});
|
|
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) =>
|
|
{
|
|
bool wantsLongUpdateCheck = mLongUpdateProfileId != 0;
|
|
ToggleCheck(menu, ref wantsLongUpdateCheck);
|
|
mLastLongUpdateCheck = 0;
|
|
mLastLongUpdateCheckError = 0;
|
|
if (wantsLongUpdateCheck)
|
|
mLongUpdateProfileId = Profiler.StartSampling("LongUpdate");
|
|
else
|
|
{
|
|
mLongUpdateProfileId.Dispose();
|
|
mLongUpdateProfileId = 0;
|
|
}
|
|
}, null, null, true, (mLongUpdateProfileId != 0) ? 1 : 0);
|
|
}
|
|
|
|
//////////
|
|
|
|
var testMenu = root.AddMenuItem("&Test");
|
|
var testRunMenu = testMenu.AddMenuItem("&Run", null, new => UpdateMenuItem_DebugStopped_HasWorkspace);
|
|
AddMenuItem(testRunMenu, "&Normal Tests", "Run Normal Tests");
|
|
AddMenuItem(testRunMenu, "&All Tests", "Run All Tests");
|
|
|
|
var testDebugMenu = testMenu.AddMenuItem("&Debug", null, new => UpdateMenuItem_DebugStopped_HasWorkspace);
|
|
AddMenuItem(testDebugMenu, "&Normal Tests", "Debug Normal Tests");
|
|
AddMenuItem(testDebugMenu, "&All Tests", "Debug All Tests");
|
|
|
|
AddMenuItem(testMenu, "Enable Console", "Test Enable Console", null, null, true, mTestEnableConsole ? 1 : 0);
|
|
|
|
//////////
|
|
|
|
mWindowMenu = root.AddMenuItem("&Window");
|
|
AddMenuItem(mWindowMenu, "&Close", "Close Window", new => UpdateMenuItem_HasActivePanel);
|
|
AddMenuItem(mWindowMenu, "&Close All", "Close All Windows");
|
|
AddMenuItem(mWindowMenu, "&New View into File", "View New", new => UpdateMenuItem_HasActiveDocument);
|
|
AddMenuItem(mWindowMenu, "&Split View", "View Split", new => UpdateMenuItem_HasActiveDocument);
|
|
|
|
subMenu = root.AddMenuItem("&Help");
|
|
AddMenuItem(subMenu, "&About", "About");
|
|
}
|
|
|
|
IDETabbedView CreateTabbedView()
|
|
{
|
|
return new IDETabbedView(null);
|
|
}
|
|
|
|
public void SetupNewWindow(WidgetWindow window, bool isMainWindow)
|
|
{
|
|
window.mOnWindowKeyDown.Add(new => SysKeyDown);
|
|
if (isMainWindow)
|
|
window.mOnWindowCloseQuery.Add(new => SecondaryAllowClose);
|
|
}
|
|
|
|
DarkTabbedView FindDocumentTabbedView()
|
|
{
|
|
for (int32 windowIdx = 0; windowIdx < mWindows.Count; windowIdx++)
|
|
{
|
|
var window = mWindows[windowIdx];
|
|
var widgetWindow = window as WidgetWindow;
|
|
if (widgetWindow != null)
|
|
{
|
|
var darkDockingFrame = widgetWindow.mRootWidget as DarkDockingFrame;
|
|
if (widgetWindow == mMainWindow)
|
|
darkDockingFrame = mDockingFrame;
|
|
|
|
//DarkTabbedView documentTabbedView = null;
|
|
|
|
DarkTabbedView documentTabbedView = null;
|
|
if (darkDockingFrame != null)
|
|
{
|
|
darkDockingFrame.WithAllDockedWidgets(scope [&] (dockedWidget) =>
|
|
{
|
|
bool hadSource = false;
|
|
var tabbedView = dockedWidget as DarkTabbedView;
|
|
if (tabbedView != null)
|
|
{
|
|
tabbedView.WithTabs(scope [&] (tab) =>
|
|
{
|
|
if (documentTabbedView != null)
|
|
return;
|
|
var content = tab.mContent;
|
|
if ((content is SourceViewPanel) ||
|
|
(content is DisassemblyPanel))
|
|
hadSource = true;
|
|
});
|
|
}
|
|
if (hadSource)
|
|
documentTabbedView = tabbedView;
|
|
});
|
|
}
|
|
if (documentTabbedView != null)
|
|
return documentTabbedView;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public DarkTabbedView GetDefaultDocumentTabbedView()
|
|
{
|
|
if ((mActiveDocumentsTabbedView == null) || (mActiveDocumentsTabbedView.mParent == null))
|
|
{
|
|
mActiveDocumentsTabbedView = CreateTabbedView();
|
|
mActiveDocumentsTabbedView.SetRequestedSize(150, 150);
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
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();
|
|
});
|
|
});
|
|
}
|
|
|
|
public void WithDocumentTabbedViews(delegate void(DarkTabbedView) func)
|
|
{
|
|
for (int32 windowIdx = 0; windowIdx < mWindows.Count; windowIdx++)
|
|
{
|
|
var window = mWindows[windowIdx];
|
|
var widgetWindow = window as WidgetWindow;
|
|
if (widgetWindow != null)
|
|
{
|
|
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)
|
|
func(tabbedView);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void EnsureDocumentArea()
|
|
{
|
|
GetDefaultDocumentTabbedView();
|
|
}
|
|
|
|
public Widget GetActivePanel()
|
|
{
|
|
if (mRunningTestScript)
|
|
return mLastActivePanel;
|
|
|
|
for (var window in mWindows)
|
|
{
|
|
if (!window.mHasFocus)
|
|
continue;
|
|
|
|
var widgetWindow = window as WidgetWindow;
|
|
if (widgetWindow != null)
|
|
{
|
|
var focusWidget = widgetWindow.mFocusWidget;
|
|
|
|
if ((focusWidget == null) && (var hoverWatch = widgetWindow.mRootWidget as HoverWatch))
|
|
{
|
|
return hoverWatch.mTextPanel;
|
|
}
|
|
|
|
while ((focusWidget != null) && (focusWidget.mParent != null))
|
|
{
|
|
if (focusWidget.mParent is TabbedView)
|
|
return focusWidget;
|
|
focusWidget = focusWidget.mParent;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
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)
|
|
return sewc;
|
|
}
|
|
}
|
|
|
|
var activeTextPanel = GetActivePanel() as TextPanel;
|
|
if (activeTextPanel != null)
|
|
{
|
|
return activeTextPanel.EditWidget.mEditWidgetContent as SourceEditWidgetContent;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public WidgetWindow GetActiveWindow()
|
|
{
|
|
for (let window in mWindows)
|
|
if (window.mHasFocus)
|
|
{
|
|
var result = window;
|
|
while ((result.mWindowFlags.HasFlag(.Modal)) && (result.mParent != null))
|
|
result = result.mParent;
|
|
return result as WidgetWindow;
|
|
}
|
|
return mMainWindow;
|
|
}
|
|
|
|
public Widget GetActiveDocumentPanel()
|
|
{
|
|
var activePanel = GetActivePanel();
|
|
if ((activePanel is SourceViewPanel) || (activePanel is DisassemblyPanel))
|
|
return activePanel;
|
|
return null;
|
|
}
|
|
|
|
public void WithTabs(delegate void(TabbedView.TabButton) func)
|
|
{
|
|
WithDocumentTabbedViews(scope (documentTabbedView) =>
|
|
{
|
|
documentTabbedView.WithTabs(func);
|
|
});
|
|
}
|
|
|
|
public TabbedView.TabButton GetTab(Widget content)
|
|
{
|
|
TabbedView.TabButton tab = null;
|
|
WithTabs(scope [&] (checkTab) =>
|
|
{
|
|
if (checkTab.mContent == content)
|
|
tab = checkTab;
|
|
});
|
|
return tab;
|
|
}
|
|
|
|
public void WithSourceViewPanels(delegate void(SourceViewPanel) func)
|
|
{
|
|
WithTabs(scope (tab) =>
|
|
{
|
|
var sourceViewPanel = tab.mContent as SourceViewPanel;
|
|
if (sourceViewPanel != null)
|
|
func(sourceViewPanel);
|
|
});
|
|
}
|
|
|
|
TabbedView.TabButton SetupTab(TabbedView tabView, String name, float width, Widget content, bool ownsContent) // 2
|
|
{
|
|
TabbedView.TabButton tabButton = tabView.AddTab(name, width, content, ownsContent);
|
|
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)
|
|
{
|
|
DisassemblyPanel disassemblyPanel = null;
|
|
WithTabs(scope [&] (tab) =>
|
|
{
|
|
if ((disassemblyPanel == null) && (tab.mContent is DisassemblyPanel))
|
|
{
|
|
disassemblyPanel = (DisassemblyPanel)tab.mContent;
|
|
disassemblyPanel.ClearQueuedData();
|
|
tab.Activate();
|
|
}
|
|
});
|
|
if (disassemblyPanel != null)
|
|
return disassemblyPanel;
|
|
|
|
TabbedView tabbedView = GetDefaultDocumentTabbedView();
|
|
disassemblyPanel = new DisassemblyPanel();
|
|
//diassemblyPanel.Show(filePath);
|
|
|
|
var newTabButton = new SourceViewTab();
|
|
newTabButton.Label = DisassemblyPanel.sPanelName;
|
|
newTabButton.mWantWidth = newTabButton.GetWantWidth();
|
|
newTabButton.mHeight = tabbedView.mTabHeight;
|
|
newTabButton.mContent = disassemblyPanel;
|
|
tabbedView.AddTab(newTabButton);
|
|
|
|
newTabButton.mCloseClickedEvent.Add(new () => CloseDocument(disassemblyPanel));
|
|
newTabButton.Activate();
|
|
//diassemblyPanel.FocusEdit();
|
|
|
|
mLastActivePanel = disassemblyPanel;
|
|
return disassemblyPanel;
|
|
}
|
|
|
|
public class SourceViewTab : DarkTabbedView.DarkTabButton
|
|
{
|
|
public float GetWantWidth()
|
|
{
|
|
return DarkTheme.sDarkTheme.mSmallFont.GetWidth(mLabel) + DarkTheme.GetScaled(40);
|
|
}
|
|
|
|
public override void Activate(bool setFocus = true)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void Draw(Graphics g)
|
|
{
|
|
base.Draw(g);
|
|
|
|
if (mWidth < mWantWidth / 2)
|
|
return;
|
|
|
|
var sourceViewPanel = mContent as SourceViewPanel;
|
|
if (sourceViewPanel != null)
|
|
{
|
|
if (sourceViewPanel.HasUnsavedChanges())
|
|
{
|
|
g.SetFont(IDEApp.sApp.mTinyCodeFont);
|
|
g.DrawString("*", mWantWidth - DarkTheme.sUnitSize + GS!(2), 0);
|
|
}
|
|
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));
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void MouseDown(float x, float y, int32 btn, int32 btnCount)
|
|
{
|
|
base.MouseDown(x, y, btn, btnCount);
|
|
|
|
if (btn == 1)
|
|
{
|
|
Menu menu = new Menu();
|
|
if (var sourceViewPanel = mContent as SourceViewPanel)
|
|
{
|
|
var item = menu.AddItem("Copy Full Path");
|
|
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);
|
|
|
|
let process = scope SpawnedProcess();
|
|
process.Start(procInfo).IgnoreError();
|
|
});
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if ((mIsRightTab) && (btn == 0) && (btnCount > 1))
|
|
{
|
|
IDEApp.sApp.MakeTabPermanent(this);
|
|
}
|
|
}
|
|
|
|
public override void Update()
|
|
{
|
|
base.Update();
|
|
Point point;
|
|
if (DarkTooltipManager.CheckMouseover(this, 25, out point))
|
|
{
|
|
var sourceViewPanel = mContent as SourceViewPanel;
|
|
if (sourceViewPanel != null)
|
|
DarkTooltipManager.ShowTooltip(sourceViewPanel.mFilePath, this, point.x, 14);
|
|
}
|
|
}
|
|
}
|
|
|
|
public SourceViewPanel FindSourceViewPanel(String filePath)
|
|
{
|
|
if (filePath == null)
|
|
return null;
|
|
String useFilePath = scope String(filePath);
|
|
if (!IDEUtils.FixFilePath(useFilePath))
|
|
return null;
|
|
|
|
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);
|
|
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;
|
|
//mEditWidget.mVertScrollbar.mScrollIncrement = editWidgetContent.mFont.GetLineSpacing();
|
|
editWidgetContent.mHiliteColor = 0xFF384858;
|
|
editWidgetContent.mUnfocusedHiliteColor = 0x80384858;
|
|
|
|
return editWidget;
|
|
}
|
|
|
|
public bool CreateEditDataEditWidget(FileEditData editData)
|
|
{
|
|
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')
|
|
{
|
|
char8 nextC = 0;
|
|
if (i < text.Length - 1)
|
|
{
|
|
nextC = text[++i];
|
|
if (nextC == 0)
|
|
{
|
|
if (i < text.Length - 2)
|
|
nextC = text[++i];
|
|
}
|
|
}
|
|
|
|
if (nextC == '\n')
|
|
editData.mLineEndingKind = .CrLf;
|
|
else
|
|
editData.mLineEndingKind = .Cr;
|
|
break;
|
|
}
|
|
else if (c == '\n')
|
|
{
|
|
editData.mLineEndingKind = .Lf;
|
|
break;
|
|
}
|
|
}
|
|
editData.BuildHash(text);
|
|
} ) case .Err)
|
|
return false;
|
|
|
|
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;
|
|
}
|
|
|
|
public FileEditData GetEditData(String filePath, bool createEditData = true, bool createEditDataWidget = true)
|
|
{
|
|
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)
|
|
CreateEditDataEditWidget(editData);
|
|
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;
|
|
if (mFileEditData.TryGet(oldFixedFilePath, out outKey, out editData))
|
|
{
|
|
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;
|
|
}
|
|
|
|
public FileEditData GetEditData(ProjectSource projectSource, bool createEditWidget = true, SourceHash.Kind hashKind = .MD5)
|
|
{
|
|
using (mMonitor.Enter())
|
|
{
|
|
if (projectSource.mEditData == null)
|
|
{
|
|
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);
|
|
/*if (projectSource.mSavedContent == null)
|
|
{
|
|
editData = CreateEditData(filePath);
|
|
}
|
|
else
|
|
{
|
|
editData = new FileEditData();
|
|
editData.mFilePath = new String(filePath);
|
|
editData.mEditWidget = CreateSourceEditWidget();
|
|
editData.mEditWidget.Content.AppendText(projectSource.mSavedContent);
|
|
editData.mEditWidget.Content.mData.mTextIdData.DuplicateFrom(projectSource.mSavedCharIdData);
|
|
editData.mOwnsEditWidget = true;
|
|
}*/
|
|
|
|
if (editData != null)
|
|
{
|
|
editData.Ref();
|
|
mFileEditData.Add(editData);
|
|
projectSource.mEditData = editData;
|
|
projectSource.mEditData.mLastFileTextVersion = projectSource.mEditData.mEditWidget.Content.mData.mCurTextVersionId;
|
|
}
|
|
}
|
|
return projectSource.mEditData;*/
|
|
}
|
|
|
|
if (createEditWidget)
|
|
CreateEditDataEditWidget(projectSource.mEditData);
|
|
return projectSource.mEditData;
|
|
}
|
|
|
|
public SourceViewPanel ShowSourceFile(String filePath, ProjectSource projectSource = null, SourceShowType showType = SourceShowType.ShowExisting, bool setFocus = true)
|
|
{
|
|
//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);
|
|
}
|
|
|
|
|
|
if ((useFilePath != null) && (!IDEUtils.FixFilePath(useFilePath)))
|
|
return null;
|
|
if ((useFilePath == null) & (showType != .New))
|
|
return null;
|
|
|
|
SourceViewPanel sourceViewPanel = null;
|
|
DarkTabbedView.DarkTabButton sourceViewPanelTab = null;
|
|
|
|
if ((useProjectSource == null) && (useFilePath != null))
|
|
{
|
|
useProjectSource = FindProjectSourceItem(useFilePath);
|
|
if (useProjectSource != null)
|
|
{
|
|
var projectSourcePath = scope:: String();
|
|
useProjectSource.GetFullImportPath(projectSourcePath);
|
|
useFilePath = projectSourcePath;
|
|
}
|
|
}
|
|
|
|
if (showType != SourceShowType.New)
|
|
{
|
|
delegate void(TabbedView.TabButton) tabFunc = scope [&] (tabButton) =>
|
|
{
|
|
var darkTabButton = (DarkTabbedView.DarkTabButton)tabButton;
|
|
if (tabButton.mContent is SourceViewPanel)
|
|
{
|
|
var checkSourceViewPanel = (SourceViewPanel)tabButton.mContent;
|
|
|
|
if (checkSourceViewPanel.FileNameMatches(useFilePath))
|
|
{
|
|
if (sourceViewPanel != null)
|
|
{
|
|
// Already found one that matches our active tabbed view?
|
|
if (sourceViewPanelTab.mTabbedView == mActiveDocumentsTabbedView)
|
|
return;
|
|
}
|
|
|
|
sourceViewPanel = checkSourceViewPanel;
|
|
sourceViewPanelTab = darkTabButton;
|
|
}
|
|
}
|
|
};
|
|
|
|
if ((showType == .ShowExistingInActivePanel) && (mActiveDocumentsTabbedView != null))
|
|
{
|
|
mActiveDocumentsTabbedView.WithTabs(tabFunc);
|
|
}
|
|
else
|
|
WithTabs(tabFunc);
|
|
|
|
if (sourceViewPanelTab != null)
|
|
{
|
|
//matchedTabButton = tabButton;
|
|
if ((sourceViewPanelTab.mIsRightTab) && (showType != SourceShowType.Temp))
|
|
{
|
|
MakeTabPermanent(sourceViewPanelTab);
|
|
}
|
|
|
|
if ((useProjectSource != null) &&
|
|
(sourceViewPanel.mProjectSource != useProjectSource))
|
|
{
|
|
//TODO: Change project source in view
|
|
sourceViewPanel.AttachToProjectSource(useProjectSource);
|
|
//sourceViewPanel.mProjectSource = useProjectSource;
|
|
//sourceViewPanel.QueueFullRefresh(true);
|
|
}
|
|
|
|
if ((sourceViewPanel.mWidgetWindow != null) && (!HasModalDialogs()) && (!mRunningTestScript))
|
|
sourceViewPanel.mWidgetWindow.SetForeground();
|
|
sourceViewPanelTab.Activate(setFocus);
|
|
sourceViewPanelTab.mTabbedView.FinishTabAnim();
|
|
if (setFocus)
|
|
sourceViewPanel.FocusEdit();
|
|
}
|
|
}
|
|
|
|
if (sourceViewPanel != null)
|
|
return sourceViewPanel;
|
|
|
|
//ShowSourceFile(filePath, projectSource, showTemp, setFocus);
|
|
|
|
DarkTabbedView tabbedView = GetDefaultDocumentTabbedView();
|
|
sourceViewPanel = new SourceViewPanel();
|
|
bool success;
|
|
if (useProjectSource != null)
|
|
{
|
|
success = sourceViewPanel.Show(useProjectSource, !mInitialized);
|
|
}
|
|
else
|
|
success = sourceViewPanel.Show(useFilePath, !mInitialized);
|
|
|
|
if (!success)
|
|
{
|
|
sourceViewPanel.Close();
|
|
delete sourceViewPanel;
|
|
return null;
|
|
}
|
|
|
|
var newTabButton = new SourceViewTab();
|
|
newTabButton.Label = "";
|
|
if (useFilePath != null)
|
|
Path.GetFileName(useFilePath, newTabButton.mLabel);
|
|
else
|
|
newTabButton.mLabel.Set("untitled");
|
|
newTabButton.mWantWidth = newTabButton.GetWantWidth();
|
|
newTabButton.mHeight = tabbedView.mTabHeight;
|
|
newTabButton.mContent = sourceViewPanel;
|
|
|
|
if (showType == SourceShowType.Temp)
|
|
{
|
|
let prevAutoClose = tabbedView.mAutoClose;
|
|
defer { tabbedView.mAutoClose = prevAutoClose; }
|
|
tabbedView.mAutoClose = false;
|
|
|
|
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);
|
|
newTabButton.mCloseClickedEvent.Add(new () => DocumentCloseClicked(sourceViewPanel));
|
|
newTabButton.Activate(setFocus);
|
|
if (setFocus)
|
|
sourceViewPanel.FocusEdit();
|
|
|
|
return sourceViewPanel;
|
|
}
|
|
|
|
int GetRecentFilesIdx(String filePath)
|
|
{
|
|
return mRecentlyDisplayedFiles.FindIndex(scope (item) => Path.Equals(item, filePath));
|
|
}
|
|
|
|
public void UpdateRecentFilesMenuItems(List<String> filesList)
|
|
{
|
|
|
|
}
|
|
|
|
public void UpdateRecentDisplayedFilesMenuItems()
|
|
{
|
|
if (mWindowMenu == null)
|
|
return;
|
|
|
|
RecentFiles.UpdateMenu(mRecentlyDisplayedFiles, mWindowMenu, mRecentlyDisplayedFilesMenuItems, true, scope (idx, sysMenu) =>
|
|
{
|
|
sysMenu.mOnMenuItemSelected.Add(new (evt) => ShowRecentFile(idx));
|
|
});
|
|
}
|
|
|
|
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) =>
|
|
{
|
|
sysMenu.mOnMenuItemSelected.Add(new (evt) => ShowRecentFile(recentKind, idx));
|
|
});
|
|
|
|
entry.mMenu.SetDisabled(entry.mMenuItems.Count == 0);
|
|
}
|
|
|
|
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)
|
|
{
|
|
//int idx = mRecentFilesList.IndexOf(path);
|
|
RecentFiles.Add(mRecentlyDisplayedFiles, path);
|
|
UpdateRecentDisplayedFilesMenuItems();
|
|
}
|
|
|
|
void ShowRecentFile(int idx, bool setFocus = true)
|
|
{
|
|
if (idx >= mRecentlyDisplayedFiles.Count)
|
|
return;
|
|
|
|
String sourceFile = mRecentlyDisplayedFiles[idx];
|
|
if (sourceFile == DisassemblyPanel.sPanelName)
|
|
{
|
|
ShowDisassemblyPanel();
|
|
return;
|
|
}
|
|
ShowSourceFile(sourceFile, null, SourceShowType.ShowExisting, setFocus);
|
|
}
|
|
|
|
void ShowRecentFile(RecentFiles.RecentKind recentKind, int idx, bool setFocus = true)
|
|
{
|
|
String filePath = mSettings.mRecentFiles.mRecents[(int)recentKind].mList[idx];
|
|
|
|
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:
|
|
}
|
|
}
|
|
|
|
void DocumentCloseClicked(Widget documentPanel)
|
|
{
|
|
var sourceViewPanel = documentPanel as SourceViewPanel;
|
|
if (sourceViewPanel == null)
|
|
{
|
|
CloseDocument(documentPanel);
|
|
return;
|
|
}
|
|
|
|
// This is a test
|
|
// This is a test
|
|
|
|
if ((!sourceViewPanel.HasUnsavedChanges()) || (!sourceViewPanel.IsLastViewOfData()))
|
|
{
|
|
CloseDocument(sourceViewPanel);
|
|
return;
|
|
}
|
|
|
|
// This is a test
|
|
|
|
String fileName = scope String();
|
|
if (sourceViewPanel.mFilePath != null)
|
|
Path.GetFileName(sourceViewPanel.mFilePath, fileName);
|
|
else
|
|
fileName.Append("untitled");
|
|
Dialog aDialog = ThemeFactory.mDefault.CreateDialog("Save file?", StackStringFormat!("Save changes to '{0}' before closing?", fileName), DarkTheme.sDarkTheme.mIconWarning);
|
|
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);
|
|
}
|
|
|
|
public void CloseDocument(Widget documentPanel)
|
|
{
|
|
bool hasFocus = false;
|
|
var sourceViewPanel = documentPanel as SourceViewPanel;
|
|
|
|
if ((documentPanel.mWidgetWindow != null) && (documentPanel.mWidgetWindow.mFocusWidget != null))
|
|
{
|
|
if (documentPanel.mWidgetWindow.mFocusWidget.HasParent(documentPanel))
|
|
hasFocus = true;
|
|
}
|
|
|
|
/*if (sourceViewPanel != null)
|
|
hasFocus = sourceViewPanel.mEditWidget.mHasFocus;*/
|
|
|
|
if ((sourceViewPanel != null) && (sourceViewPanel.HasUnsavedChanges()))
|
|
{
|
|
// When we close a Beef file that has modified text, we need to revert by
|
|
// reparsing from the actual source file
|
|
if (sourceViewPanel.mIsBeefSource)
|
|
{
|
|
mBfResolveHelper.DeferReparse(sourceViewPanel.mFilePath, null);
|
|
//mBfResolveHelper.DeferRefreshVisibleViews(null);
|
|
}
|
|
|
|
var projectSource = sourceViewPanel.mProjectSource;
|
|
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
|
|
while (editData.HasTextChanged())
|
|
{
|
|
var nextUndoAction = editWidgetContent.mData.mUndoManager.GetLastUndoAction();
|
|
if (nextUndoAction == null)
|
|
break;
|
|
if (nextUndoAction is UndoBatchEnd)
|
|
{
|
|
var undoBatchEnd = (UndoBatchEnd)nextUndoAction;
|
|
if (undoBatchEnd.Name.StartsWith("#"))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
editWidgetContent.mData.mUndoManager.Undo();
|
|
}
|
|
|
|
editWidgetContent.mData.mTextIdData.Prepare();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
if (sourceViewPanel != null)
|
|
{
|
|
sourceViewPanel.Dispose();
|
|
if (sourceViewPanel.mFilePath != null)
|
|
recentFileIdx = GetRecentFilesIdx(sourceViewPanel.mFilePath);
|
|
}
|
|
|
|
/*if (tabButton.mIsRightTab)
|
|
tabbedView.SetRightTab(null);
|
|
else*/
|
|
|
|
if (documentPanel is DisassemblyPanel)
|
|
recentFileIdx = GetRecentFilesIdx(DisassemblyPanel.sPanelName);
|
|
//mRecentFilesList.Remove(DisassemblyPanel.sPanelName);
|
|
|
|
if (recentFileIdx != -1)
|
|
{
|
|
delete mRecentlyDisplayedFiles[recentFileIdx];
|
|
mRecentlyDisplayedFiles.RemoveAt(recentFileIdx);
|
|
UpdateRecentDisplayedFilesMenuItems();
|
|
}
|
|
|
|
if (tabButton.mIsActive)
|
|
{
|
|
// If this succeeds then tabbUtton.mIsActive will be false, otherwise we do the 'nextTab' logic below
|
|
ShowRecentFile(0);
|
|
}
|
|
|
|
TabbedView.TabButton nextTab = null;
|
|
bool foundRemovedTab = false;
|
|
// Select the previous tab or the next one (if this is the first)
|
|
if (tabButton.mIsActive)
|
|
{
|
|
tabbedView.WithTabs(scope [&] (checkTab) =>
|
|
{
|
|
if (checkTab == tabButton)
|
|
foundRemovedTab = true;
|
|
else if ((!foundRemovedTab) || (nextTab == null))
|
|
nextTab = checkTab;
|
|
});
|
|
}
|
|
|
|
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>();
|
|
|
|
/*if (mRecentFilesList.Count >= 1)
|
|
{
|
|
// Show second-last file
|
|
ShowRecentFile(0, hasFocus);
|
|
}*/
|
|
//var newActiveTab = tabbedView.GetActiveTab();
|
|
//if (newActiveTab != null)
|
|
//newActiveTab.mContent.SetFocus();
|
|
}
|
|
|
|
void TryCloseCurrentDocument()
|
|
{
|
|
var activeDocumentPanel = GetActiveDocumentPanel();
|
|
if (activeDocumentPanel != null)
|
|
{
|
|
if (activeDocumentPanel is SourceViewPanel)
|
|
{
|
|
var sourceViewPanel = (SourceViewPanel)activeDocumentPanel;
|
|
DocumentCloseClicked(sourceViewPanel);
|
|
return;
|
|
}
|
|
CloseDocument(activeDocumentPanel);
|
|
return;
|
|
}
|
|
|
|
var activePanel = GetActivePanel();
|
|
if (activePanel != null)
|
|
CloseDocument(activePanel);
|
|
}
|
|
|
|
void TryCloseAllDocuments()
|
|
{
|
|
var docPanels = scope List<Widget>();
|
|
|
|
WithTabs(scope [&] (tab) =>
|
|
{
|
|
if ((tab.mContent is SourceViewPanel) || (tab.mTabbedView.mIsFillWidget))
|
|
{
|
|
docPanels.Add(tab.mContent);
|
|
}
|
|
});
|
|
|
|
for (var docPanel in docPanels)
|
|
DocumentCloseClicked(docPanel);
|
|
}
|
|
|
|
void ViewSplit()
|
|
{
|
|
var sourceViewPanel = GetActiveSourceViewPanel();
|
|
if (sourceViewPanel != null)
|
|
{
|
|
sourceViewPanel.SplitView();
|
|
}
|
|
}
|
|
|
|
[IDECommand]
|
|
void CloseCurrentDocument()
|
|
{
|
|
var activeDocumentPanel = GetActiveDocumentPanel();
|
|
if (activeDocumentPanel != null)
|
|
CloseDocument(activeDocumentPanel);
|
|
}
|
|
|
|
public SourceViewPanel ShowProjectItem(ProjectItem projectItem, bool showTemp = true, bool setFocus = true)
|
|
{
|
|
if (projectItem is ProjectSource)
|
|
{
|
|
var projectSource = (ProjectSource)projectItem;
|
|
var fullPath = scope String();
|
|
projectSource.GetFullImportPath(fullPath);
|
|
return ShowSourceFile(fullPath, projectSource, showTemp ? SourceShowType.Temp : SourceShowType.ShowExistingInActivePanel, setFocus);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public SourceViewPanel ShowSourceFileLocation(String filePath, int showHotIdx, int refHotIdx, int line, int column, LocatorType hilitePosition, bool showTemp = false)
|
|
{
|
|
var sourceViewPanel = ShowSourceFile(filePath, null, showTemp ? SourceShowType.Temp : SourceShowType.ShowExisting);
|
|
if (sourceViewPanel == null)
|
|
return null;
|
|
sourceViewPanel.ShowHotFileIdx(showHotIdx);
|
|
sourceViewPanel.ShowFileLocation(refHotIdx, Math.Max(0, line), Math.Max(0, column), hilitePosition);
|
|
return sourceViewPanel;
|
|
}
|
|
|
|
public SourceViewPanel ShowSourceFileLocation(String filePath, int32 cursorIdx, LocatorType hilitePosition)
|
|
{
|
|
var sourceViewPanel = ShowSourceFile(filePath);
|
|
sourceViewPanel.ShowFileLocation(cursorIdx, hilitePosition);
|
|
return sourceViewPanel;
|
|
}
|
|
|
|
public void ShowPCLocation(int32 stackIdx, bool onlyShowCurrent = false, bool wantShowSource = false, bool wantShowDisassembly = false, bool setFocus = true)
|
|
{
|
|
if (stackIdx == -1)
|
|
return;
|
|
|
|
ClearDeferredUserRequest();
|
|
|
|
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();
|
|
DebugManager.FrameFlags frameFlags;
|
|
|
|
for (int pass < 2)
|
|
{
|
|
mDebugger.GetStackFrameInfo(stackIdx, entryName, out addr, filePath, out hotIdx, out defLineStart, out defLineEnd, out line, out column, out language, out stackSize, out frameFlags);
|
|
|
|
if (!frameFlags.HasFlag(.HasPendingDebugInfo))
|
|
break;
|
|
|
|
int bangPos = entryName.IndexOf('!');
|
|
if (bangPos == -1)
|
|
break;
|
|
|
|
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;
|
|
|
|
for (var breakpoint in mDebugger.mBreakpointList)
|
|
{
|
|
if (((breakpoint.mAddressRequested) || (breakpoint.mInstrOffset != -1)) &&
|
|
(breakpoint.ContainsAddress(addr)))
|
|
useWantShowDisassembly = true;
|
|
}
|
|
|
|
String aliasFilePath = null;
|
|
String loadCmd = null;
|
|
|
|
SourceHash hash = default;
|
|
int hashPos = filePath.IndexOf('#');
|
|
bool checkForOldFileInfo = false;
|
|
if (hashPos != -1)
|
|
{
|
|
let hashStr = StringView(filePath, hashPos + 1);
|
|
hash = SourceHash.Create(hashStr);
|
|
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();
|
|
|
|
if (fileHash != hash)
|
|
checkForOldFileInfo = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!File.Exists(filePath))
|
|
checkForOldFileInfo = true;
|
|
}
|
|
|
|
if (checkForOldFileInfo)
|
|
{
|
|
String outFileInfo = scope String();
|
|
mDebugger.GetStackFrameOldFileInfo(mDebugger.mActiveCallStackIdx, outFileInfo);
|
|
|
|
var args = outFileInfo.Split!('\n');
|
|
if (args.Count == 3)
|
|
{
|
|
aliasFilePath = scope:: String(filePath);
|
|
|
|
filePath.Set(args[0]);
|
|
loadCmd = scope:: String(args[1]);
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
bool hasSource = filePath.Length != 0;
|
|
if ((IsInDisassemblyMode(wantShowSource)) || (!hasSource) || (useWantShowDisassembly))
|
|
{
|
|
if (frameFlags.HasFlag(.HasPendingDebugInfo))
|
|
{
|
|
SetDeferredUserRequest(new DeferredShowPCLocation(stackIdx));
|
|
}
|
|
else
|
|
{
|
|
var disassemblyPanel = ShowDisassemblyPanel(true);
|
|
if (aliasFilePath != null)
|
|
String.NewOrSet!(disassemblyPanel.mAliasFilePath, aliasFilePath);
|
|
disassemblyPanel.Show(addr, filePath, line, column, hotIdx, defLineStart, defLineEnd);
|
|
disassemblyPanel.mSourceHash = hash;
|
|
}
|
|
}
|
|
else if (filePath.Length > 0)
|
|
{
|
|
if ((IsCrashDump) && (!mShowedFirstDocument))
|
|
{
|
|
mShowedFirstDocument = true;
|
|
ShowCallstack();
|
|
}
|
|
|
|
var sourceViewPanel = ShowSourceFile(filePath, null, SourceShowType.ShowExisting, setFocus);
|
|
if (sourceViewPanel != null)
|
|
{
|
|
sourceViewPanel.mIsSourceCode = true; // It's always source code, even if there is no extension (ie: stl types like "vector")
|
|
|
|
if ((aliasFilePath != null) && (sourceViewPanel.mAliasFilePath == null))
|
|
String.NewOrSet!(sourceViewPanel.mAliasFilePath, aliasFilePath);
|
|
|
|
|
|
|
|
if (sourceViewPanel.mLoadFailed)
|
|
{
|
|
sourceViewPanel.mWantHash = hash;
|
|
if (loadCmd != null)
|
|
{
|
|
sourceViewPanel.SetLoadCmd(loadCmd);
|
|
}
|
|
}
|
|
else if ((gApp.mDebugger.mIsRunningWithHotSwap) && (sourceViewPanel.mIsBeefSource))
|
|
{
|
|
// No 'wrong hash' warnings
|
|
}
|
|
else if (((hash != .None) && (sourceViewPanel.mEditData != null) && (!sourceViewPanel.mEditData.CheckHash(hash))) ||
|
|
(sourceViewPanel.mHasChangedSinceLastCompile))
|
|
{
|
|
sourceViewPanel.ShowWrongHash();
|
|
}
|
|
|
|
int showHotIdx = -1;
|
|
if (!onlyShowCurrent)
|
|
{
|
|
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)
|
|
{
|
|
if (sourceViewPanel.HasTextChangedSinceCompile(defLineStart, defLineEnd, hotIdx))
|
|
showHotIdx = hotIdx;
|
|
}
|
|
/*else
|
|
{
|
|
if (sourceViewPanel.HasTextChangedSinceCompile(defLineStart, defLineEnd, -1))
|
|
showHotIdx = mWorkspace.GetHighestCompileIdx();
|
|
}*/
|
|
}
|
|
|
|
sourceViewPanel.ShowHotFileIdx(showHotIdx);
|
|
sourceViewPanel.ShowFileLocation(hotIdx, Math.Max(0, line), Math.Max(0, column), LocatorType.Smart);
|
|
}
|
|
|
|
var disassemblyPanel = TryGetDisassemblyPanel(false);
|
|
if (disassemblyPanel != null)
|
|
disassemblyPanel.Show(addr, filePath, line, column, hotIdx, defLineStart, defLineEnd);
|
|
}
|
|
}
|
|
|
|
public override void UnhandledCommandLine(String key, String value)
|
|
{
|
|
Fail(StackStringFormat!("Unhandled command line param: {0}", key));
|
|
}
|
|
|
|
public override bool HandleCommandLineParam(String key, String value)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (base.HandleCommandLineParam(key, value))
|
|
return true;
|
|
|
|
if (value == null)
|
|
{
|
|
switch (key)
|
|
{
|
|
case "-autoshutdown":
|
|
mDebugAutoShutdownCounter = 200;
|
|
case "-new":
|
|
mVerb = .New;
|
|
case "-testNoExit":
|
|
mExitWhenTestScriptDone = false;
|
|
case "-firstRun":
|
|
mForceFirstRun = true;
|
|
mIsFirstRun = true;
|
|
case "-clean":
|
|
mWantsClean = true;
|
|
case "-dbgCompileDump":
|
|
mDbgCompileDump = true;
|
|
case "-launch":
|
|
if (mLaunchData == null)
|
|
mLaunchData = new .();
|
|
case "-launchPaused":
|
|
if (mLaunchData != null)
|
|
mLaunchData.mPaused = true;
|
|
else
|
|
Fail("'-launchPaused' can only be used after '-launch'");
|
|
default:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
switch (key)
|
|
{
|
|
#if BF_PLATFORM_WINDOWS
|
|
case "-attachHandle":
|
|
mProcessAttachHandle = (Windows.EventHandle)int32.Parse(value).GetValueOrDefault();
|
|
#endif
|
|
case "-attachId":
|
|
mProcessAttachId = int32.Parse(value).GetValueOrDefault();
|
|
case "-config":
|
|
if (mSetConfigName == null)
|
|
mSetConfigName = new String();
|
|
mSetConfigName.Set(value);
|
|
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'");
|
|
#if BF_PLATFORM_WINDOWS
|
|
case "-minidump":
|
|
String.NewOrSet!(mCrashDumpPath, value);
|
|
#endif
|
|
case "-platform":
|
|
if (mSetPlatformName == null)
|
|
mSetPlatformName = new String();
|
|
mSetPlatformName.Set(value);
|
|
case "-test":
|
|
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;
|
|
mScriptManager.SetTimeoutMS(20*60*1000); // 20 minute timeout
|
|
mScriptManager.QueueCommandFile(absTestPath);
|
|
case "-verbosity":
|
|
if (value == "quiet")
|
|
mVerbosity = .Quiet;
|
|
else if (value == "minimal")
|
|
mVerbosity = .Minimal;
|
|
else if (value == "normal")
|
|
mVerbosity = .Normal;
|
|
else if (value == "detailed")
|
|
mVerbosity = .Detailed;
|
|
else if (value == "diagnostic")
|
|
mVerbosity = .Diagnostic;
|
|
else
|
|
Fail(scope String()..AppendF("Invalid verbosity option: {}", value));
|
|
case "-workspace","-proddir":
|
|
var relDir = scope String(value);
|
|
if ((relDir.EndsWith("\\")) || relDir.EndsWith("\""))
|
|
relDir.RemoveToEnd(relDir.Length - 1);
|
|
IDEUtils.FixFilePath(relDir);
|
|
|
|
String fullDir = new String();
|
|
Path.GetFullPath(relDir, fullDir);
|
|
|
|
if ((File.Exists(fullDir)) || (IsBeefFile(fullDir)))
|
|
{
|
|
mWorkspace.mCompositeFile = new CompositeFile(fullDir);
|
|
delete fullDir;
|
|
}
|
|
else
|
|
mWorkspace.mDir = fullDir;
|
|
case "-file":
|
|
String.NewOrSet!(mDeferredOpenFileName, value);
|
|
if (mDeferredOpenFileName.EndsWith(".bfdbg", .OrdinalIgnoreCase))
|
|
mDeferredOpen = .DebugSession;
|
|
else if (mDeferredOpenFileName.EndsWith(".dmp", .OrdinalIgnoreCase))
|
|
mDeferredOpen = .CrashDump;
|
|
else
|
|
mDeferredOpen = .File;
|
|
default:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
class Board : Widget
|
|
{
|
|
public override void Draw(Graphics g)
|
|
{
|
|
base.Draw(g);
|
|
|
|
using (g.PushColor(0xFFFFFFFF))
|
|
g.FillRect(0, 0, 80, 80);
|
|
}
|
|
}
|
|
|
|
public void Output(String outStr)
|
|
{
|
|
#if CLI
|
|
Console.Write(outStr);
|
|
return;
|
|
#endif
|
|
#unwarn
|
|
outStr.Replace("\r", "");
|
|
mOutputPanel.Write(outStr);
|
|
}
|
|
|
|
public void OutputSmart(String outStr)
|
|
{
|
|
#if CLI
|
|
Console.Write(outStr);
|
|
return;
|
|
#endif
|
|
#unwarn
|
|
if (outStr.Contains('\r'))
|
|
{
|
|
let newStr = scope String()..Append(outStr);
|
|
newStr.Replace("\r", "");
|
|
mOutputPanel.WriteSmart(newStr);
|
|
}
|
|
else
|
|
mOutputPanel.WriteSmart(outStr);
|
|
}
|
|
|
|
public void Output(String format, params Object[] args)
|
|
{
|
|
String outStr = format;
|
|
if (args.Count > 0)
|
|
{
|
|
outStr = scope:: String();
|
|
outStr.AppendF(outStr, params args);
|
|
}
|
|
else
|
|
outStr = scope:: String(format);
|
|
outStr.Replace("\r", "");
|
|
mOutputPanel.Write(outStr);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
public void OutputLine(String format, params Object[] args)
|
|
{
|
|
String outStr;
|
|
if (args.Count > 0)
|
|
{
|
|
outStr = scope:: String();
|
|
outStr.AppendF(format, params args);
|
|
}
|
|
else
|
|
outStr = scope:: String(format);
|
|
outStr.Replace("\r", "");
|
|
|
|
if (mRunningTestScript)
|
|
Console.WriteLine(outStr);
|
|
|
|
#if CLI
|
|
Console.WriteLine(outStr);
|
|
#else
|
|
outStr.Append("\n");
|
|
mOutputPanel.Write(outStr);
|
|
#endif
|
|
}
|
|
|
|
public void OutputErrorLine(String format, params Object[] args)
|
|
{
|
|
mWantShowOutput = true;
|
|
var errStr = scope String();
|
|
errStr.Append("ERROR: ", format);
|
|
OutputLineSmart(errStr, params args);
|
|
}
|
|
|
|
public void OutputWarnLine(String 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);
|
|
}
|
|
|
|
public void OutputLineSmart(String format, params Object[] args)
|
|
{
|
|
String outStr;
|
|
if (args.Count > 0)
|
|
{
|
|
outStr = scope:: String();
|
|
outStr.AppendF(format, params args);
|
|
}
|
|
else
|
|
outStr = scope:: String(format);
|
|
outStr.Replace("\r", "");
|
|
#if CLI
|
|
if (outStr.StartsWith("ERROR:"))
|
|
mFailed = true;
|
|
Console.WriteLine(outStr);
|
|
#else
|
|
outStr.Append("\n");
|
|
if (mOutputPanel != null)
|
|
mOutputPanel.WriteSmart(outStr);
|
|
#endif
|
|
}
|
|
|
|
public void OutputFormatted(String str, bool isDbgEvalOutput = false)
|
|
{
|
|
#if CLI
|
|
Console.Write(str);
|
|
return;
|
|
#endif
|
|
|
|
#unwarn
|
|
String useStr = str;
|
|
|
|
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 == '\'')
|
|
{
|
|
// Allow
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
int64 memoryAddress = int64.Parse(addrSB, .HexNumber);
|
|
String srcLocation = scope String();
|
|
mDebugger.GetAddressSourceLocation((int)memoryAddress, srcLocation);
|
|
|
|
var newStr = scope:: String();
|
|
newStr.Append(useStr, 0, locPos);
|
|
newStr.Append(srcLocation);
|
|
newStr.Append(useStr, i);
|
|
useStr = newStr;
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if ((isDbgEvalOutput) && (mIsImmediateDebugExprEval))
|
|
mImmediatePanel.WriteResult(useStr);
|
|
|
|
OutputSmart(useStr);
|
|
}
|
|
|
|
public void PhysSetScale(float scale, bool force = false)
|
|
{
|
|
var prevScale = DarkTheme.sScale;
|
|
float useScale = Math.Clamp(scale, 0.5f, 4.0f);
|
|
if ((prevScale == useScale) && (!force))
|
|
return;
|
|
|
|
DarkTheme.SetScale(useScale);
|
|
RehupScale();
|
|
|
|
for (int32 windowIdx = 0; windowIdx < mWindows.Count; windowIdx++)
|
|
{
|
|
var window = mWindows[windowIdx];
|
|
var widgetWindow = window as WidgetWindow;
|
|
if (widgetWindow != null)
|
|
{
|
|
widgetWindow.mRootWidget.RehupScale(prevScale, DarkTheme.sScale);
|
|
|
|
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)
|
|
{
|
|
tabbedView.WithTabs(scope (tab) =>
|
|
{
|
|
var panel = tab.mContent as Panel;
|
|
if ((panel != null) && (panel.mWidgetWindow == null))
|
|
{
|
|
panel.RehupScale(prevScale, DarkTheme.sScale);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
widgetWindow.mRootWidget.RehupSize();
|
|
}
|
|
widgetWindow.mIsDirty = true;
|
|
}
|
|
}
|
|
|
|
public void SetScale(float scale, bool force = false)
|
|
{
|
|
PhysSetScale(scale, force);
|
|
gApp.mSettings.mEditorSettings.mUIScale = DarkTheme.sScale * 100.0f;
|
|
}
|
|
|
|
void SysKeyDown(KeyDownEvent evt)
|
|
{
|
|
if (evt.mHandled)
|
|
return;
|
|
|
|
var window = (WidgetWindow)evt.mSender;
|
|
|
|
if (window.mFocusWidget is KeysEditWidget)
|
|
{
|
|
return;
|
|
}
|
|
|
|
IDECommand.ContextFlags useFlags = .None;
|
|
var activeWindow = GetActiveWindow();
|
|
bool isMainWindow = activeWindow.mRootWidget is MainFrame;
|
|
bool isWorkWindow = isMainWindow || (activeWindow.mRootWidget is DarkDockingFrame);
|
|
|
|
var activePanel = GetActivePanel() as Panel;
|
|
if (activePanel is SourceViewPanel)
|
|
useFlags |= .Editor;
|
|
else if (activePanel is DisassemblyPanel)
|
|
useFlags |= .Editor;
|
|
|
|
if (isMainWindow)
|
|
useFlags |= .MainWindow;
|
|
if (isWorkWindow)
|
|
useFlags |= .WorkWindow;
|
|
|
|
if (evt.mKeyCode == .Tab)
|
|
{
|
|
if (activePanel != null)
|
|
{
|
|
if (activePanel.HandleTab(window.IsKeyDown(.Shift) ? -1 : 1))
|
|
return;
|
|
}
|
|
}
|
|
|
|
var keyState = scope KeyState();
|
|
keyState.mKeyCode = evt.mKeyCode;
|
|
keyState.mKeyFlags = evt.mKeyFlags;
|
|
|
|
var curKeyMap = mCommands.mKeyMap;
|
|
bool hadChordState = mKeyChordState != null;
|
|
if (mKeyChordState != null)
|
|
curKeyMap = mKeyChordState.mCommandMap;
|
|
DeleteAndNullify!(mKeyChordState);
|
|
|
|
KeyState matchedKey;
|
|
IDECommandBase commandBase;
|
|
if (curKeyMap.mMap.TryGet(keyState, out matchedKey, out commandBase))
|
|
{
|
|
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)
|
|
{
|
|
bool foundMatch = false;
|
|
if (useFlags != .None)
|
|
{
|
|
var checkCommand = command;
|
|
while (checkCommand != null)
|
|
{
|
|
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);
|
|
|
|
if (matches)
|
|
{
|
|
checkCommand.mAction();
|
|
foundMatch = true;
|
|
}
|
|
checkCommand = checkCommand.mNext;
|
|
}
|
|
}
|
|
|
|
if (!foundMatch)
|
|
{
|
|
var checkCommand = command;
|
|
while (checkCommand != null)
|
|
{
|
|
if (checkCommand.mContextFlags == .None)
|
|
{
|
|
checkCommand.mAction();
|
|
foundMatch = true;
|
|
}
|
|
checkCommand = checkCommand.mNext;
|
|
}
|
|
}
|
|
|
|
if (foundMatch)
|
|
{
|
|
evt.mHandled = true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Not found
|
|
if (hadChordState)
|
|
{
|
|
Beep(.Error);
|
|
evt.mHandled = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
SourceViewPanel sourceViewPanel = null;
|
|
Widget focusWidget = window.mFocusWidget;
|
|
|
|
if (focusWidget is EditWidget)
|
|
focusWidget = focusWidget.mParent;
|
|
if (focusWidget is SourceViewPanel)
|
|
sourceViewPanel = (SourceViewPanel)focusWidget;
|
|
//if (focusWidget is DisassemblyPanel)
|
|
//break;
|
|
|
|
if (evt.mKeyFlags == 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:
|
|
//OutputLine("Unknown key: {0}", (int)evt.mKeycode);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ShowOpenFileInSolutionDialog()
|
|
{
|
|
var widgetWindow = GetCurrentWindow();
|
|
if (widgetWindow != null)
|
|
{
|
|
var openFileInSolutionDialog = new OpenFileInSolutionDialog();
|
|
openFileInSolutionDialog.PopupWindow(mMainWindow);
|
|
}
|
|
}
|
|
|
|
void ChangeCase(bool toUpper)
|
|
{
|
|
let sourceViewPanel = GetActiveSourceViewPanel();
|
|
if (sourceViewPanel == null)
|
|
return;
|
|
|
|
var ewc = sourceViewPanel.mEditWidget.mEditWidgetContent;
|
|
if (!ewc.HasSelection())
|
|
return;
|
|
|
|
/*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;
|
|
}*/
|
|
|
|
var prevSel = ewc.mSelection.Value;
|
|
|
|
var str = scope String();
|
|
ewc.GetSelectionText(str);
|
|
|
|
var prevStr = scope String();
|
|
prevStr.Append(str);
|
|
|
|
if (toUpper)
|
|
str.ToUpper();
|
|
else
|
|
str.ToLower();
|
|
|
|
if (str == prevStr)
|
|
return;
|
|
|
|
ewc.InsertAtCursor(str);
|
|
|
|
ewc.mSelection = prevSel;
|
|
}
|
|
|
|
public bool IsFilteredOut(String fileName)
|
|
{
|
|
if (fileName.Contains('~'))
|
|
return true;
|
|
if (fileName.EndsWith(".TMP", .OrdinalIgnoreCase))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
public static bool IsBeefFile(String fileName)
|
|
{
|
|
return fileName.EndsWith(".cs", StringComparison.OrdinalIgnoreCase) || fileName.EndsWith(".bf", StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
public static bool IsClangSourceFile(String fileName)
|
|
{
|
|
#if IDE_C_SUPPORT
|
|
return fileName.EndsWith(".cpp", StringComparison.OrdinalIgnoreCase) || fileName.EndsWith(".c", StringComparison.OrdinalIgnoreCase) || fileName.EndsWith(".cc", StringComparison.OrdinalIgnoreCase);
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
public static bool IsSourceCode(String fileName)
|
|
{
|
|
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);
|
|
}
|
|
|
|
public static bool IsClangSourceHeader(String fileName)
|
|
{
|
|
return fileName.EndsWith(".h", StringComparison.OrdinalIgnoreCase) || fileName.EndsWith(".hpp", StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
public void GetBeefPreprocessorMacros(DefinesSet macroList)
|
|
{
|
|
let platform = Workspace.PlatformType.GetFromName(mPlatformName);
|
|
if (platform != .Unknown)
|
|
{
|
|
String def = scope .();
|
|
def.Append("BF_PLATFORM_");
|
|
platform.ToString(def);
|
|
def.ToUpper();
|
|
macroList.Add(def);
|
|
}
|
|
|
|
var workspaceOptions = GetCurWorkspaceOptions();
|
|
if (workspaceOptions.mRuntimeChecks)
|
|
macroList.Add("BF_RUNTIME_CHECKS");
|
|
if (workspaceOptions.mEnableObjectDebugFlags)
|
|
macroList.Add("BF_ENABLE_OBJECT_DEBUG_FLAGS");
|
|
if (workspaceOptions.mAllowHotSwapping)
|
|
macroList.Add("BF_ALLOW_HOT_SWAPPING");
|
|
|
|
bool is64Bits = Workspace.PlatformType.GetPtrSizeByName(gApp.mPlatformName) == 8;
|
|
|
|
if (is64Bits)
|
|
{
|
|
if (workspaceOptions.mLargeStrings)
|
|
macroList.Add("BF_LARGE_STRINGS");
|
|
if (workspaceOptions.mLargeCollections)
|
|
macroList.Add("BF_LARGE_COLLECTIONS");
|
|
}
|
|
|
|
// Only supported on Windows at the moment
|
|
bool hasLeakCheck = false;
|
|
if (workspaceOptions.LeakCheckingEnabled)
|
|
{
|
|
hasLeakCheck = true;
|
|
macroList.Add("BF_ENABLE_REALTIME_LEAK_CHECK");
|
|
}
|
|
|
|
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)
|
|
macroList.Add("BF_HAS_VDATA_EXTENDER");
|
|
}
|
|
|
|
public void GetClangResolveArgs(Project project, List<String> outResolveArgs)
|
|
{
|
|
using (project.mMonitor.Enter())
|
|
{
|
|
//List<string> argsList = new List<string>();
|
|
var options = GetCurProjectOptions(project);
|
|
|
|
if (options == null)
|
|
return;
|
|
|
|
outResolveArgs.Add(new String("-std=c++11"));
|
|
|
|
if (options.mCOptions.mEnableBeefInterop)
|
|
{
|
|
var beefPreprocMacros = scope DefinesSet();
|
|
GetBeefPreprocessorMacros(beefPreprocMacros);
|
|
for (var beefPreprocMacro in beefPreprocMacros.mDefines)
|
|
{
|
|
outResolveArgs.Add(new String("-D", beefPreprocMacro));
|
|
}
|
|
}
|
|
|
|
for (var preprocMacro in options.mCOptions.mPreprocessorMacros)
|
|
outResolveArgs.Add(new String("-D", preprocMacro));
|
|
for (var includePath in options.mCOptions.mIncludePaths)
|
|
{
|
|
//outResolveArgs.Add(new String("-I", includePath));
|
|
var fullIncludePath = scope String();
|
|
project.GetProjectFullPath(includePath, fullIncludePath);
|
|
outResolveArgs.Add(new String("-I", fullIncludePath));
|
|
}
|
|
|
|
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"));
|
|
|
|
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"));*/
|
|
}
|
|
}
|
|
|
|
public CompilerBase GetProjectCompilerForFile(String fileName)
|
|
{
|
|
if (IsBeefFile(fileName))
|
|
return mBfResolveCompiler;
|
|
#if IDE_C_SUPPORT
|
|
if (IsClangSourceFile(fileName))
|
|
return mDepClang;
|
|
#endif
|
|
return null;
|
|
}
|
|
|
|
public CompilerBase GetResolveCompilerForFile(String fileName)
|
|
{
|
|
if (IsBeefFile(fileName))
|
|
return mBfResolveCompiler;
|
|
#if IDE_C_SUPPORT
|
|
if (IsClangSourceFile(fileName))
|
|
return mResolveClang;
|
|
#endif
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*void StopExecutionInstance()
|
|
{
|
|
if (mExecutionInstance != null)
|
|
{
|
|
try
|
|
{
|
|
mExecutionInstance.mProcess.Kill();
|
|
}
|
|
catch (Exception)
|
|
{
|
|
}
|
|
}
|
|
}*/
|
|
|
|
const int cArgFileThreshold = 0x7800;
|
|
|
|
public ExecutionQueueCmd QueueRun(String fileName, String args, String workingDir, ArgsFileKind argsFileKind = .None)
|
|
{
|
|
var executionQueueCmd = new ExecutionQueueCmd();
|
|
executionQueueCmd.mFileName = new String(fileName);
|
|
executionQueueCmd.mArgs = new String(args);
|
|
executionQueueCmd.mWorkingDir = new String(workingDir);
|
|
if (args.Length > cArgFileThreshold)
|
|
{
|
|
// Only use UTF16 if we absolutely need to
|
|
if ((argsFileKind == .UTF16WithBom) && (!args.HasMultibyteChars()))
|
|
executionQueueCmd.mUseArgsFile = .UTF8;
|
|
else
|
|
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));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void WriteInputThread(Object obj)
|
|
{
|
|
ExecutionInstance executionInstance = (ExecutionInstance)obj;
|
|
|
|
FileStream fileStream = scope FileStream();
|
|
if (executionInstance.mProcess.AttachStandardInput(fileStream) case .Err)
|
|
return;
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
while (true)
|
|
{
|
|
var buffer = scope String();
|
|
if (streamReader.ReadLine(buffer) case .Err)
|
|
break;
|
|
using (IDEApp.sApp.mMonitor.Enter())
|
|
executionInstance.mDeferredOutput.Add(new String(buffer));
|
|
}
|
|
}
|
|
|
|
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)
|
|
break;
|
|
|
|
using (IDEApp.sApp.mMonitor.Enter())
|
|
executionInstance.mDeferredOutput.Add(new String(buffer));
|
|
}
|
|
}
|
|
|
|
public enum RunFlags
|
|
{
|
|
None,
|
|
ShellCommand = 1,
|
|
BatchCommand = 2,
|
|
NoRedirect = 4,
|
|
NoWait = 8,
|
|
}
|
|
|
|
public ExecutionInstance DoRun(String inFileName, String args, String workingDir, ArgsFileKind useArgsFile, Dictionary<String, String> envVars = null, String stdInData = null, RunFlags runFlags = .None)
|
|
{
|
|
//Debug.Assert(executionInstance == null);
|
|
|
|
String fileName = scope String(inFileName ?? "");
|
|
QuoteIfNeeded(fileName);
|
|
|
|
ProcessStartInfo startInfo = scope ProcessStartInfo();
|
|
startInfo.UseShellExecute = false;
|
|
if (!fileName.IsEmpty)
|
|
startInfo.SetFileName(fileName);
|
|
startInfo.SetWorkingDirectory(workingDir);
|
|
startInfo.SetArguments(args);
|
|
|
|
if ((!runFlags.HasFlag(.NoRedirect)) && (!runFlags.HasFlag(.NoWait)))
|
|
{
|
|
startInfo.RedirectStandardOutput = true;
|
|
startInfo.RedirectStandardError = true;
|
|
startInfo.RedirectStandardInput = true;
|
|
startInfo.CreateNoWindow = true;
|
|
}
|
|
|
|
|
|
var executionInstance = new ExecutionInstance();
|
|
|
|
#if BF_PLATFORM_WINDOWS
|
|
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("/c ");
|
|
IDEUtils.AppendWithOptionalQuotes(shellArgs, fileName);
|
|
shellArgs.Append(" ");
|
|
shellArgs.Append(args);
|
|
startInfo.SetFileName("cmd.exe");
|
|
startInfo.SetArguments(shellArgs);
|
|
}
|
|
#endif
|
|
|
|
if (envVars != null)
|
|
{
|
|
for (var envKV in envVars)
|
|
startInfo.AddEnvironmentVariable(envKV.key, envKV.value);
|
|
}
|
|
|
|
if (useArgsFile != .None)
|
|
{
|
|
String tempFileName = scope String();
|
|
Path.GetTempFileName(tempFileName);
|
|
|
|
Encoding encoding = Encoding.UTF8;
|
|
if (useArgsFile == .UTF16WithBom)
|
|
encoding = Encoding.UTF16WithBOM;
|
|
|
|
var result = File.WriteAllText(tempFileName, args, encoding);
|
|
if (result case .Err)
|
|
OutputLine("Failed to create temporary param file");
|
|
String arguments = scope String();
|
|
arguments.Concat("@", tempFileName);
|
|
startInfo.SetArguments(arguments);
|
|
|
|
delete executionInstance.mTempFileName;
|
|
executionInstance.mTempFileName = new String(tempFileName);
|
|
}
|
|
|
|
if (mVerbosity >= .Detailed)
|
|
{
|
|
String showArgs = startInfo.[Friend]mArguments;
|
|
if ((mRunningTestScript) && (showArgs.Length > 1024))
|
|
{
|
|
showArgs = scope:: String(showArgs, 0, 1024);
|
|
showArgs.Append("...");
|
|
}
|
|
|
|
if (!startInfo.[Friend]mFileName.IsEmpty)
|
|
{
|
|
OutputLine("Executing: {0} {1}", startInfo.[Friend]mFileName, showArgs);
|
|
if ((mVerbosity >= .Diagnostic) && (useArgsFile != .None))
|
|
OutputLine("Arg file contents: {0}", args);
|
|
}
|
|
else
|
|
OutputLine("Executing: {0}", showArgs);
|
|
}
|
|
|
|
//if (useArgsFile)
|
|
{
|
|
//if (mVerbosity >= .Detailed)
|
|
//OutputLine(" Args: {0}", args);
|
|
}
|
|
|
|
/*var processResult = Process.Start(startInfo);
|
|
if (case .Err = processResult)
|
|
{
|
|
OutputLine("Failed to execute \"{0}\"", fileName);
|
|
return executionInstance;
|
|
}*/
|
|
|
|
SpawnedProcess process = new SpawnedProcess();
|
|
if (process.Start(startInfo) case .Err)
|
|
{
|
|
OutputLine("Failed to execute \"{0}\"", inFileName);
|
|
delete process;
|
|
delete executionInstance;
|
|
return null;
|
|
}
|
|
|
|
if (runFlags.HasFlag(.NoWait))
|
|
{
|
|
delete process;
|
|
delete executionInstance;
|
|
return null;
|
|
}
|
|
|
|
mExecutionInstances.Add(executionInstance);
|
|
executionInstance.mStopwatch.Start();
|
|
executionInstance.mProcess = process;
|
|
|
|
executionInstance.mOutputThread = new Thread(new => ReadOutputThread);
|
|
executionInstance.mOutputThread.Start(executionInstance, false);
|
|
|
|
executionInstance.mErrorThread = new Thread(new => ReadErrorThread);
|
|
executionInstance.mErrorThread.Start(executionInstance, false);
|
|
|
|
if (stdInData != null)
|
|
{
|
|
executionInstance.mStdInData = new String(stdInData);
|
|
executionInstance.mInputThread = new Thread(new => WriteInputThread);
|
|
executionInstance.mInputThread.Start(executionInstance, false);
|
|
}
|
|
|
|
return executionInstance;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
void UpdateExecution()
|
|
{
|
|
if (mExecutionInstances.Count > 0)
|
|
{
|
|
var executionInstance = mExecutionInstances[0];
|
|
bool failed = false;
|
|
bool isDone = false;
|
|
|
|
using (mMonitor.Enter())
|
|
{
|
|
for (var str in executionInstance.mDeferredOutput)
|
|
{
|
|
OutputLine(str);
|
|
delete str;
|
|
}
|
|
executionInstance.mDeferredOutput.Clear();
|
|
}
|
|
|
|
if (executionInstance.mProcess == null)
|
|
{
|
|
// Didn't even get off the ground
|
|
isDone = true;
|
|
failed = true;
|
|
executionInstance.mExitCode = -1;
|
|
}
|
|
else if (executionInstance.mProcess.HasExited)
|
|
{
|
|
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;
|
|
if (executionInstance.mProcess.ExitCode != 0)
|
|
failed = true;
|
|
executionInstance.mProcess.WaitFor(-1);
|
|
executionInstance.mProcess.Close();
|
|
|
|
executionInstance.mStopwatch.Stop();
|
|
if (executionInstance.mIsTargetRun)
|
|
{
|
|
mTargetExitCode = executionInstance.mExitCode;
|
|
}
|
|
else
|
|
{
|
|
if (executionInstance.mParallelGroup == -1)
|
|
{
|
|
if (mVerbosity >= .Detailed)
|
|
OutputLine("Execution time: {0:0.00}s", executionInstance.mStopwatch.ElapsedMilliseconds / 1000.0f);
|
|
}
|
|
|
|
if (executionInstance.mCanceled)
|
|
OutputLine("Execution Canceled");
|
|
else if (failed)
|
|
OutputLine("Execution Failed");
|
|
}
|
|
|
|
if (executionInstance.mTempFileName != null)
|
|
{
|
|
File.Delete(executionInstance.mTempFileName).IgnoreError();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (failed)
|
|
{
|
|
if (mExecutionQueue.Count > 0)
|
|
{
|
|
var buildCompletedCmd = GetBuildCompletedCmd();
|
|
if (buildCompletedCmd != null)
|
|
buildCompletedCmd.mFailed = true;
|
|
}
|
|
}
|
|
|
|
if (isDone)
|
|
{
|
|
mExecutionInstances.RemoveAt(0);
|
|
if (executionInstance.mAutoDelete)
|
|
delete executionInstance;
|
|
}
|
|
|
|
/*if (failed)
|
|
{
|
|
OutputLine("COMPILATION FAILED");
|
|
mExecutionQueue.Clear();
|
|
}
|
|
|
|
if ((executionInstance == null) && (mExecutionQueue.Count == 0))
|
|
{
|
|
OutputLine("Compilation finished.");
|
|
}*/
|
|
}
|
|
|
|
bool buildFailed = false;
|
|
if ((mBuildContext != null) && (mBuildContext.Failed))
|
|
buildFailed = true;
|
|
let buildCompleteCmd = GetBuildCompletedCmd();
|
|
if ((buildCompleteCmd != null) && (buildCompleteCmd.mFailed))
|
|
buildFailed = true;
|
|
|
|
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];
|
|
|
|
bool waitForBuildClang = false;
|
|
#if IDE_C_SUPPORT
|
|
waitForBuildClang = (mDepClang.mCompileWaitsForQueueEmpty) && (mDepClang.HasQueuedCommands());
|
|
#endif
|
|
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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (ignoreCommand)
|
|
{
|
|
// 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);
|
|
}
|
|
}
|
|
else if (next is ProcessBfCompileCmd)
|
|
{
|
|
var processCompileCmd = (ProcessBfCompileCmd)next;
|
|
|
|
mCompilingBeef = false;
|
|
BeefCompileDone();
|
|
|
|
//processCompileCmd.mStopwatch.Stop();
|
|
if ((processCompileCmd.mHadBeef) && (mVerbosity >= .Normal))
|
|
OutputLine("Beef compilation time: {0:0.00}s", processCompileCmd.mStopwatch.ElapsedMilliseconds / 1000.0f);
|
|
|
|
var compileInstance = mWorkspace.mCompileInstanceList.Back;
|
|
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;
|
|
}
|
|
|
|
ProcessBeefCompileResults(processCompileCmd.mBfPassInstance, processCompileCmd.mCompileKind, processCompileCmd.mHotProject, processCompileCmd.mStopwatch);
|
|
if (mHotResolveState != .None)
|
|
{
|
|
if (compileInstance.mCompileResult == .Success)
|
|
compileInstance.mCompileResult = .PendingHotLoad;
|
|
}
|
|
|
|
if (processCompileCmd.mProfileCmd != null)
|
|
{
|
|
mExecutionQueue.Add(processCompileCmd.mProfileCmd);
|
|
processCompileCmd.mProfileCmd = null;
|
|
}
|
|
|
|
delete processCompileCmd.mStopwatch;
|
|
processCompileCmd.mStopwatch = null;
|
|
}
|
|
else if (next is TargetCompletedCmd)
|
|
{
|
|
if (!buildFailed)
|
|
{
|
|
var targetCompletedCmd = (TargetCompletedCmd)next;
|
|
targetCompletedCmd.mProject.mNeedsTargetRebuild = false;
|
|
targetCompletedCmd.mProject.mForceCustomCommands = false;
|
|
}
|
|
}
|
|
else if (next is StartDebugCmd)
|
|
{
|
|
if (!buildFailed)
|
|
{
|
|
var startDebugCmd = (StartDebugCmd)next;
|
|
if (StartupProject(true, startDebugCmd.mWasCompiled))
|
|
{
|
|
OutputLine("Debugger started");
|
|
}
|
|
else
|
|
OutputLine("Failed to start debugger");
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
|
|
var executionInstance = DoRun(executionQueueCmd.mFileName, executionQueueCmd.mArgs, executionQueueCmd.mWorkingDir, executionQueueCmd.mUseArgsFile, executionQueueCmd.mEnvVars, executionQueueCmd.mStdInData, executionQueueCmd.mRunFlags);
|
|
if (executionInstance != null)
|
|
{
|
|
executionInstance.mParallelGroup = executionQueueCmd.mParallelGroup;
|
|
executionInstance.mIsTargetRun = executionQueueCmd.mIsTargetRun;
|
|
}
|
|
}
|
|
else if (next is BuildCompletedCmd)
|
|
{
|
|
var buildCompletedCmd = (BuildCompletedCmd)next;
|
|
#if IDE_C_SUPPORT
|
|
mWorkspace.WithProjectItems(scope (projectItem) =>
|
|
{
|
|
var projectSource = projectItem as ProjectSource;
|
|
if (projectItem != null)
|
|
{
|
|
String fullPath = scope String();
|
|
projectSource.GetFullImportPath(fullPath);
|
|
if (completedCompileCmd.mClangCompiledFiles.Contains(fullPath))
|
|
{
|
|
mDepClang.QueueCheckDependencies(projectSource, ClangCompiler.DepCheckerType.Clang);
|
|
}
|
|
}
|
|
});
|
|
if (!completedCompileCmd.mFailed)
|
|
mDepClang.mDoDependencyCheck = false;
|
|
#endif
|
|
if (buildFailed)
|
|
buildCompletedCmd.mFailed = true;
|
|
|
|
CompileResult(buildCompletedCmd.mHotProjectName, !buildCompletedCmd.mFailed);
|
|
|
|
if (buildCompletedCmd.mFailed)
|
|
OutputLineSmart("ERROR: BUILD FAILED");
|
|
if ((mVerbosity >= .Detailed) && (buildCompletedCmd.mStopwatch != null))
|
|
OutputLine("Total build time: {0:0.00}s", buildCompletedCmd.mStopwatch.ElapsedMilliseconds / 1000.0f);
|
|
|
|
CompileDone(!buildCompletedCmd.mFailed);
|
|
|
|
if (mTestManager != null)
|
|
{
|
|
if (buildCompletedCmd.mFailed)
|
|
mTestManager.BuildFailed();
|
|
else
|
|
mTestManager.Start();
|
|
}
|
|
}
|
|
else if (var profileCmd = next as ProfileCmd)
|
|
{
|
|
if (gApp.mDebugger.mIsRunning)
|
|
mProfilePanel.StartProfiling(profileCmd.mThreadId, profileCmd.mDesc, profileCmd.mSampleRate);
|
|
}
|
|
else
|
|
{
|
|
Runtime.FatalError("Unknown command");
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool ParseSourceFile(BfSystem bfSystem, BfPassInstance passInstance, ProjectSource projectSource)
|
|
{
|
|
bool worked = true;
|
|
|
|
if (!IsBeefFile(projectSource.mPath))
|
|
return true;
|
|
|
|
var fullPath = scope String();
|
|
projectSource.GetFullImportPath(fullPath);
|
|
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);
|
|
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();
|
|
projectSource.GetFullImportPath(fullPath);
|
|
|
|
//SourceViewPanel sourceViewPanel = null;
|
|
|
|
using (mMonitor.Enter())
|
|
{
|
|
if (projectSource.mEditData != null)
|
|
{
|
|
if (projectSource.mEditData.IsFileDeleted())
|
|
{
|
|
return false;
|
|
}
|
|
if (projectSource.mEditData.mEditWidget != null)
|
|
{
|
|
var idData = ref projectSource.mEditData.mEditWidget.Content.mData.mTextIdData;
|
|
idData.Prepare();
|
|
if (!projectSource.mEditData.mSavedCharIdData.Equals(idData))
|
|
{
|
|
char8IdData = projectSource.mEditData.mEditWidget.mEditWidgetContent.mData.mTextIdData.Duplicate();
|
|
projectSource.mEditData.mEditWidget.GetText(sourceContent);
|
|
if (sourceHash != null)
|
|
{
|
|
*sourceHash = SourceHash.Create(.MD5, sourceContent, projectSource.mEditData.mLineEndingKind);
|
|
if (*sourceHash case .MD5(let md5Hash))
|
|
projectSource.mEditData.mMD5Hash = md5Hash;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (projectSource.mEditData.mSavedContent != null)
|
|
{
|
|
char8IdData = projectSource.mEditData.mSavedCharIdData.Duplicate();
|
|
sourceContent.Set(projectSource.mEditData.mSavedContent);
|
|
if ((!projectSource.mEditData.mMD5Hash.IsZero) && (sourceHash != null))
|
|
*sourceHash = .MD5(projectSource.mEditData.mMD5Hash);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (loadOnFail)
|
|
{
|
|
String text = scope String();
|
|
bool isValid = false;
|
|
if (LoadTextFile(fullPath, text, true, scope [&] () => { if (sourceHash != null) *sourceHash = SourceHash.Create(.MD5, text); } ) case .Ok)
|
|
{
|
|
mFileWatcher.FileIsValid(fullPath);
|
|
isValid = true;
|
|
}
|
|
|
|
char8IdData = IdSpan.GetDefault((int32)text.Length);
|
|
var editData = GetEditData(projectSource, false);
|
|
using (mMonitor.Enter())
|
|
{
|
|
if (isValid)
|
|
{
|
|
editData.SetSavedData(text, char8IdData);
|
|
sourceContent.Set(text);
|
|
}
|
|
else
|
|
{
|
|
editData.SetSavedData(null, IdSpan());
|
|
editData.mFileDeleted = true;
|
|
}
|
|
if (sourceHash != null)
|
|
{
|
|
if (*sourceHash case .MD5(let md5Hash))
|
|
editData.mMD5Hash = md5Hash;
|
|
}
|
|
}
|
|
return isValid;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public void SetInDisassemblyView(bool inDisassemblyView)
|
|
{
|
|
if (mShuttingDown)
|
|
return;
|
|
|
|
if (mInDisassemblyView != inDisassemblyView)
|
|
{
|
|
// We need to refresh auto watch panel so we can handle displaying registers when switching to disasm
|
|
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;
|
|
}
|
|
|
|
public bool HasPendingBeefFiles(bool forceQueue, ProjectFolder projectFolder)
|
|
{
|
|
bool hadBeef = false;
|
|
|
|
for (var item in projectFolder.mChildItems)
|
|
{
|
|
if (item.mIncludeKind == .Ignore)
|
|
continue;
|
|
|
|
if (item is ProjectSource)
|
|
{
|
|
var projectSource = (ProjectSource)item;
|
|
if (IsBeefFile(projectSource.mPath))
|
|
{
|
|
if ((projectSource.HasChangedSinceLastCompile) || (forceQueue))
|
|
{
|
|
hadBeef = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (item is ProjectFolder)
|
|
{
|
|
var innerProjectFolder = (ProjectFolder)item;
|
|
hadBeef |= HasPendingBeefFiles(forceQueue, innerProjectFolder);
|
|
}
|
|
}
|
|
|
|
return hadBeef;
|
|
}
|
|
|
|
public bool QueueParseBeefFiles(BfCompiler bfCompiler, bool forceQueue, ProjectFolder projectFolder, Project hotProject)
|
|
{
|
|
bool hadBeef = false;
|
|
|
|
for (var item in projectFolder.mChildItems)
|
|
{
|
|
if (item.mIncludeKind == .Ignore)
|
|
continue;
|
|
|
|
if (item is ProjectSource)
|
|
{
|
|
var projectSource = (ProjectSource)item;
|
|
if (IsBeefFile(projectSource.mPath))
|
|
{
|
|
// 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);
|
|
}
|
|
}
|
|
else // Actual build
|
|
{
|
|
bool wantsHashRefresh = false;
|
|
if ((hotProject == null) && (projectSource.mWasBuiltWithOldHash))
|
|
wantsHashRefresh = true;
|
|
|
|
if ((projectSource.HasChangedSinceLastCompile) || (projectSource.mLoadFailed) || (forceQueue) || (wantsHashRefresh))
|
|
{
|
|
// 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);
|
|
sourceHash = compileInstance.mSourceHash;
|
|
}
|
|
}
|
|
|
|
bfCompiler.QueueProjectSource(projectSource, sourceHash, !bfCompiler.mIsResolveOnly);
|
|
hadBeef = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (item is ProjectFolder)
|
|
{
|
|
var innerProjectFolder = (ProjectFolder)item;
|
|
hadBeef |= QueueParseBeefFiles(bfCompiler, forceQueue, innerProjectFolder, hotProject);
|
|
}
|
|
}
|
|
|
|
return hadBeef;
|
|
}
|
|
|
|
#if IDE_C_SUPPORT
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
public Workspace.Options GetCurWorkspaceOptions()
|
|
{
|
|
return mWorkspace.GetOptions(mConfigName, mPlatformName);
|
|
}
|
|
|
|
public Workspace.ConfigSelection GetCurConfigSelection(Project project)
|
|
{
|
|
var workspaceOptions = mWorkspace.GetOptions(mConfigName, mPlatformName);
|
|
if (workspaceOptions == null)
|
|
return null;
|
|
|
|
Workspace.ConfigSelection configSelection;
|
|
workspaceOptions.mConfigSelections.TryGetValue(project, out configSelection);
|
|
if ((configSelection == null) || (!configSelection.mEnabled))
|
|
return null;
|
|
|
|
return configSelection;
|
|
}
|
|
|
|
public Project.Options GetCurProjectOptions(Project project)
|
|
{
|
|
if (project.mFailed)
|
|
return null;
|
|
|
|
Workspace.ConfigSelection configSelection = GetCurConfigSelection(project);
|
|
if (configSelection == null)
|
|
return null;
|
|
return project.GetOptions(configSelection.mConfig, configSelection.mPlatform);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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
|
|
// Can only be changed from the Resolve BfCompiler thread, and Build settings
|
|
// are only changed before background compilation begins. We also call this
|
|
// during WorkspaceLoad, but the resolve threads aren't processing then.
|
|
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)
|
|
{
|
|
//Fail(StackStringFormat!("Failed to retrieve options for {0}", project.mProjectName));
|
|
bfProject.SetDisabled(true);
|
|
return false;
|
|
}
|
|
|
|
bfProject.SetDisabled(false);
|
|
|
|
let platform = Workspace.PlatformType.GetFromName(mPlatformName);
|
|
|
|
var preprocessorMacros = scope DefinesSet();
|
|
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);
|
|
|
|
var optimizationLevel = workspaceOptions.mBfOptimizationLevel;
|
|
if (options.mBeefOptions.mOptimizationLevel != null)
|
|
optimizationLevel = options.mBeefOptions.mOptimizationLevel.Value;
|
|
|
|
if ((optimizationLevel == .OgPlus) && (mPlatformName != "Win64") && (bfCompiler == mBfBuildCompiler))
|
|
{
|
|
OutputLineSmart("WARNING: Project '{0}' has Og+ specified, which is only supported for Win64 targets.", project.mProjectName);
|
|
optimizationLevel = .O0;
|
|
}
|
|
|
|
BuildOptions.LTOType ltoType = .None;
|
|
|
|
if (!bfCompiler.mIsResolveOnly)
|
|
{
|
|
ltoType = workspaceOptions.mLTOType;
|
|
if (options.mBeefOptions.mLTOType != null)
|
|
ltoType = options.mBeefOptions.mLTOType.Value;
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (workspaceOptions.mBuildKind == .Test)
|
|
{
|
|
targetType = .BeefTest;
|
|
if (mTestManager != null)
|
|
mTestManager.AddProject(project);
|
|
}
|
|
else
|
|
{
|
|
OutputErrorLine("Project '{0}' has a Test configuration specified but the workspace is not using a Test configuration", project.mProjectName);
|
|
success = false;
|
|
}
|
|
}
|
|
else if (options.mBuildOptions.mBuildKind == .StaticLib)
|
|
{
|
|
if (project.mGeneralOptions.mTargetType.IsBeefApplication)
|
|
targetType = .BeefApplication_StaticLib;
|
|
}
|
|
else if (options.mBuildOptions.mBuildKind == .DynamicLib)
|
|
{
|
|
if (project.mGeneralOptions.mTargetType.IsBeefApplication)
|
|
targetType = .BeefApplication_DynamicLib;
|
|
}
|
|
}
|
|
|
|
var relocType = options.mBeefOptions.mRelocType;
|
|
|
|
if (relocType == .NotSet)
|
|
{
|
|
if (platform != .Windows)
|
|
relocType = .PIC;
|
|
}
|
|
|
|
bfProject.SetOptions(targetType,
|
|
project.mBeefGlobalOptions.mStartupObject,
|
|
preprocessorMacros.mDefines,
|
|
optimizationLevel, ltoType, relocType, options.mBeefOptions.mPICLevel,
|
|
options.mBeefOptions.mMergeFunctions, options.mBeefOptions.mCombineLoads,
|
|
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);
|
|
}
|
|
|
|
if (!bfCompiler.mIsResolveOnly)
|
|
{
|
|
for (let typeOption in project.mBeefGlobalOptions.mDistinctBuildOptions)
|
|
bfSystem.AddTypeOptions(typeOption);
|
|
for (let typeOption in options.mBeefOptions.mDistinctBuildOptions)
|
|
bfSystem.AddTypeOptions(typeOption);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
public void CurrentWorkspaceConfigChanged()
|
|
{
|
|
#if IDE_C_SUPPORT
|
|
for (var val in mDepClang.mProjectBuildString.Values)
|
|
delete val;
|
|
mDepClang.mProjectBuildString.Clear();
|
|
#endif
|
|
|
|
if (mBfResolveCompiler != null)
|
|
{
|
|
mBfResolveCompiler.QueueSetWorkspaceOptions(null, 0);
|
|
mBfResolveCompiler.QueueDeferredResolveAll();
|
|
}
|
|
|
|
mWorkspace.FixOptions(mConfigName, mPlatformName);
|
|
for (var project in mWorkspace.mProjects)
|
|
{
|
|
ProjectOptionsChanged(project);
|
|
#if IDE_C_SUPPORT
|
|
QueueParseClangFiles(mDepClang, project.mRootFolder);
|
|
#endif
|
|
}
|
|
mWorkspace.ClearProjectNameCache();
|
|
mProjectPanel.RehupProjects();
|
|
}
|
|
|
|
/*public string GetClangDepConfigName(Project project)
|
|
{
|
|
string[] clangArgs = GetClangResolveArgs(project);
|
|
string clangArgsStr = String.Join("\n", clangArgs);
|
|
|
|
long hash = 0;
|
|
for (int i = 0; i < clangArgsStr.Length; i++)
|
|
hash = (hash << 5) - hash + clangArgsStr[i];
|
|
return String.Format("{0:X16}", hash);
|
|
}*/
|
|
|
|
public void ProjectOptionsChanged(Project project, bool reparseFiles = true)
|
|
{
|
|
bool isEnabled = IsProjectEnabled(project);
|
|
mWorkspace.ClearProjectNameCache();
|
|
|
|
if (mBfResolveCompiler != null)
|
|
{
|
|
mBfResolveCompiler.QueueSetupProjectSettings(project);
|
|
}
|
|
|
|
if (isEnabled != project.mEnabled)
|
|
{
|
|
project.mEnabled = isEnabled;
|
|
|
|
if (isEnabled)
|
|
{
|
|
QueueProjectItems(project);
|
|
}
|
|
else
|
|
{
|
|
RemoveProjectItems(project);
|
|
}
|
|
|
|
mBfResolveCompiler.QueueDeferredResolveAll();
|
|
mBfResolveCompiler.QueueRefreshViewCommand();
|
|
return;
|
|
}
|
|
|
|
#if IDE_C_SUPPORT
|
|
mDepClang.mDoDependencyCheck = true;
|
|
#endif
|
|
if (mBfResolveCompiler != null)
|
|
{
|
|
if (IsProjectEnabled(project))
|
|
{
|
|
if (reparseFiles)
|
|
QueueParseBeefFiles(mBfResolveCompiler, false, project.mRootFolder, null);
|
|
mBfResolveCompiler.QueueDeferredResolveAll();
|
|
mBfResolveCompiler.QueueRefreshViewCommand();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (reparseFiles)
|
|
QueueParseBeefFiles(mBfResolveCompiler, false, project.mRootFolder, null);
|
|
}
|
|
}
|
|
|
|
public void RenameProject(Project project, String newName)
|
|
{
|
|
mWantsBeefClean = true;
|
|
|
|
for (var checkProject in mWorkspace.mProjects)
|
|
{
|
|
for (var dep in checkProject.mDependencies)
|
|
{
|
|
if (dep.mProjectName == project.mProjectName)
|
|
{
|
|
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);
|
|
project.SetChanged();
|
|
mWorkspace.ClearProjectNameCache();
|
|
}
|
|
|
|
public void RemoveProject(Project project)
|
|
{
|
|
RemoveProjectItems(project);
|
|
|
|
mWorkspace.SetChanged();
|
|
mWorkspace.mProjects.Remove(project);
|
|
#if IDE_C_SUPPORT
|
|
using (mDepClang.mMonitor.Enter())
|
|
{
|
|
mDepClang.mProjectBuildString.Remove(project);
|
|
}
|
|
#endif
|
|
|
|
if (mWorkspace.mStartupProject == project)
|
|
mWorkspace.mStartupProject = null;
|
|
|
|
mWorkspace.FixOptions();
|
|
CurrentWorkspaceConfigChanged();
|
|
|
|
var bfCompilers = scope List<BfCompiler>();
|
|
GetBfCompilers(bfCompilers);
|
|
for (var bfCompiler in bfCompilers)
|
|
{
|
|
var bfProject = bfCompiler.mBfSystem.GetBfProject(project);
|
|
bfProject.SetDisabled(true);
|
|
bfCompiler.mBfSystem.RemoveBfProject(project);
|
|
bfCompiler.QueueDeleteBfProject(bfProject);
|
|
}
|
|
|
|
for (var checkProject in mWorkspace.mProjects)
|
|
{
|
|
for (var dep in checkProject.mDependencies)
|
|
{
|
|
if (dep.mProjectName == project.mProjectName)
|
|
{
|
|
checkProject.SetChanged();
|
|
@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);
|
|
|
|
IdleDeferDelete(project);
|
|
}
|
|
|
|
BfPassInstance CompileBeef(Project hotProject, int32 hotIdx, bool lastCompileHadMessages, out bool hadBeef)
|
|
{
|
|
CompilerLog("IDEApp.CompileBeef");
|
|
|
|
hadBeef = false;
|
|
mCompilingBeef = true;
|
|
BeefCompileStarted();
|
|
|
|
bool success = true;
|
|
BfSystem bfSystem = mBfBuildSystem;
|
|
BfCompiler bfCompiler = mBfBuildCompiler;
|
|
BfPassInstance passInstance = bfSystem.CreatePassInstance();
|
|
bfCompiler.QueueSetPassInstance(passInstance);
|
|
|
|
bfCompiler.QueueSetWorkspaceOptions(hotProject, hotIdx);
|
|
|
|
Workspace.Options workspaceOptions = GetCurWorkspaceOptions();
|
|
|
|
bool tryQueueFiles = true;
|
|
bool doCompile = false;
|
|
if (lastCompileHadMessages)
|
|
doCompile = true;
|
|
if ((!workspaceOptions.mIncrementalBuild) && (!lastCompileHadMessages))
|
|
{
|
|
tryQueueFiles = false;
|
|
for (var project in mWorkspace.mProjects)
|
|
{
|
|
if (HasPendingBeefFiles(false, project.mRootFolder))
|
|
tryQueueFiles = true;
|
|
}
|
|
}
|
|
|
|
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);
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (!success)
|
|
{
|
|
bfCompiler.QueueDeletePassInstance(passInstance);
|
|
return null;
|
|
}
|
|
|
|
for (var project in mWorkspace.mProjects)
|
|
{
|
|
if (!project.mGeneralOptions.mTargetType.IsBeef)
|
|
continue;
|
|
|
|
hadBeef = true;
|
|
String projectBuildDir = scope String();
|
|
GetWorkspaceBuildDir(projectBuildDir);
|
|
projectBuildDir.Append("/", project.mProjectName);
|
|
Directory.CreateDirectory(projectBuildDir).IgnoreError();
|
|
}
|
|
|
|
if (!hadBeef)
|
|
doCompile = false;
|
|
|
|
if (doCompile)
|
|
{
|
|
for (var project in mWorkspace.mProjects)
|
|
{
|
|
// Regenerate these
|
|
DeleteContainerAndItems!(project.mCurBfOutputFileNames);
|
|
project.mCurBfOutputFileNames = null;
|
|
}
|
|
|
|
var dir = scope String();
|
|
GetWorkspaceBuildDir(dir);
|
|
bfCompiler.QueueCompile(dir);
|
|
}
|
|
else
|
|
{
|
|
bfCompiler.ClearResults();
|
|
passInstance.mCompileSucceeded = true;
|
|
}
|
|
return passInstance;
|
|
}
|
|
|
|
public bool DoResolveConfigString(String platformName, Workspace.Options workspaceOptions, Project project, Project.Options options, StringView configString, String error, String result)
|
|
{
|
|
int i = result.Length;
|
|
result.Append(configString);
|
|
|
|
bool hadError = false;
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
prevC = c;
|
|
}
|
|
|
|
if (parenPos != -1)
|
|
ReplaceBlock:
|
|
do
|
|
{
|
|
String replaceStr = scope String(result, i + 2, parenPos - i - 2);
|
|
String newString = null;
|
|
|
|
if (replaceStr.Contains(' '))
|
|
{
|
|
String cmd = scope .();
|
|
|
|
List<String> args = scope .();
|
|
|
|
for (let str in replaceStr.Split(' ', .RemoveEmptyEntries))
|
|
{
|
|
if (cmd.IsEmpty)
|
|
cmd.Set(str);
|
|
else
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
String cmdErr = null;
|
|
|
|
switch (cmd)
|
|
{
|
|
case "Slash":
|
|
if (args.Count == 1)
|
|
{
|
|
newString = scope:ReplaceBlock .();
|
|
args[0].QuoteString(newString);
|
|
}
|
|
else
|
|
cmdErr = "Invalid number of arguments";
|
|
case "Var":
|
|
break ReplaceBlock;
|
|
}
|
|
|
|
if (newString == null)
|
|
{
|
|
if (error != null)
|
|
{
|
|
if (cmdErr != null)
|
|
error.Set(cmdErr);
|
|
else
|
|
error.Set(replaceStr);
|
|
}
|
|
hadError = true;
|
|
break ReplaceBlock;
|
|
}
|
|
}
|
|
|
|
if ((newString == null) && (project != null))
|
|
{
|
|
switch (replaceStr)
|
|
{
|
|
case "ProjectName":
|
|
newString = project.mProjectName;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((newString == null) && (options != null))
|
|
{
|
|
switch (replaceStr)
|
|
{
|
|
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":
|
|
{
|
|
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();
|
|
Path.GetAbsolutePath(targetDir, project.mProjectDir, newString);
|
|
IDEUtils.FixFilePath(newString);
|
|
}
|
|
case "TargetPath":
|
|
{
|
|
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);
|
|
newString = scope:ReplaceBlock String();
|
|
Path.GetAbsolutePath(targetDir, project.mProjectDir, newString);
|
|
Utils.GetDirWithSlash(newString);
|
|
|
|
if (!DoResolveConfigString(platformName, workspaceOptions, project, options, options.mBuildOptions.mTargetName, error, newString))
|
|
return false;
|
|
|
|
let platformType = Workspace.PlatformType.GetFromName(platformName);
|
|
|
|
if (options.mBuildOptions.mBuildKind.IsApplicationLib)
|
|
{
|
|
switch (platformType)
|
|
{
|
|
case .Windows:
|
|
newString.Append(".lib");
|
|
case .iOS:
|
|
if (options.mBuildOptions.mBuildKind == .DynamicLib)
|
|
newString.Append(".dylib");
|
|
else
|
|
newString.Append(".a");
|
|
default:
|
|
if (options.mBuildOptions.mBuildKind == .DynamicLib)
|
|
newString.Append(".so");
|
|
else
|
|
newString.Append(".a");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (platformType)
|
|
{
|
|
case .Windows:
|
|
if (project.mGeneralOptions.mTargetType == .BeefLib)
|
|
newString.Append(".lib");
|
|
else if (project.mGeneralOptions.mTargetType == .BeefDynLib)
|
|
newString.Append(".dll");
|
|
else if (project.mGeneralOptions.mTargetType != .CustomBuild)
|
|
newString.Append(".exe");
|
|
case .macOS:
|
|
if (project.mGeneralOptions.mTargetType == .BeefDynLib)
|
|
newString.Append(".dylib");
|
|
default:
|
|
if (project.mGeneralOptions.mTargetType == .BeefDynLib)
|
|
newString.Append(".so");
|
|
}
|
|
}
|
|
}
|
|
IDEUtils.FixFilePath(newString);
|
|
case "ProjectDir":
|
|
if (project.IsDebugSession)
|
|
{
|
|
newString = scope:ReplaceBlock String();
|
|
Path.GetDirectoryPath(project.mProjectPath, newString);
|
|
}
|
|
else
|
|
newString = project.mProjectDir;
|
|
IDEUtils.FixFilePath(newString);
|
|
case "BuildDir":
|
|
newString = scope:ReplaceBlock String();
|
|
GetProjectBuildDir(project, newString);
|
|
IDEUtils.FixFilePath(newString);
|
|
//Debug.WriteLine("BuildDir: {0}", newString);
|
|
case "LinkFlags":
|
|
newString = scope:ReplaceBlock String();
|
|
|
|
if ((project.mGeneralOptions.mTargetType == .BeefConsoleApplication) ||
|
|
(project.mGeneralOptions.mTargetType == .BeefGUIApplication) ||
|
|
(project.mGeneralOptions.mTargetType == .BeefDynLib) ||
|
|
(options.mBuildOptions.mBuildKind == .Test))
|
|
{
|
|
let platformType = Workspace.PlatformType.GetFromName(platformName);
|
|
String rtName = scope String();
|
|
String dbgName = scope String();
|
|
BuildContext.GetRtLibNames(platformType, workspaceOptions, options, false, rtName, dbgName);
|
|
|
|
switch (platformType)
|
|
{
|
|
case .Windows:
|
|
newString.Append(rtName);
|
|
if (!dbgName.IsEmpty)
|
|
newString.Append(" ", dbgName);
|
|
switch (workspaceOptions.mAllocType)
|
|
{
|
|
case .JEMalloc:
|
|
newString.Append(" jemalloc.lib");
|
|
case .TCMalloc:
|
|
newString.Append(" tcmalloc.lib");
|
|
default:
|
|
}
|
|
case .macOS:
|
|
newString.AppendF("./{} -Wl,-rpath -Wl,@executable_path", rtName);
|
|
case .iOS:
|
|
case .Linux:
|
|
newString.AppendF("./{} -lpthread -ldl -Wl,-rpath -Wl,$ORIGIN", rtName);
|
|
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":
|
|
newString = gApp.mSettings.mVSSettings.mBin32Path;
|
|
IDEUtils.FixFilePath(newString);
|
|
case "VSToolPath_x64":
|
|
newString = gApp.mSettings.mVSSettings.mBin64Path;
|
|
IDEUtils.FixFilePath(newString);
|
|
}
|
|
}
|
|
|
|
if ((newString == null) && (mScriptManager != null))
|
|
{
|
|
switch (replaceStr)
|
|
{
|
|
case "ScriptDir":
|
|
if ((mScriptManager != null) && (mScriptManager.mCurCmd != null))
|
|
{
|
|
newString = scope:ReplaceBlock String();
|
|
Path.GetDirectoryPath(mScriptManager.mCurCmd.mSrcFile, newString);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (newString == null)
|
|
{
|
|
switch (replaceStr)
|
|
{
|
|
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:
|
|
}
|
|
}
|
|
|
|
if (newString == null)
|
|
{
|
|
if (error != null)
|
|
error.Set(replaceStr);
|
|
hadError = true;
|
|
break;
|
|
}
|
|
|
|
if (newString != null)
|
|
{
|
|
result.Remove(i, parenPos - i + 1);
|
|
result.Insert(i, newString);
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return !hadError;
|
|
}
|
|
|
|
public void ReplaceVariables(String result)
|
|
{
|
|
int i = 0;
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
prevC = c;
|
|
}
|
|
|
|
if (parenPos != -1)
|
|
ReplaceBlock:
|
|
do
|
|
{
|
|
String replaceStr = scope String(result, i + 2, parenPos - i - 2);
|
|
|
|
if (!replaceStr.StartsWith("Var "))
|
|
continue;
|
|
|
|
String varName = scope String(replaceStr, 4);
|
|
String newString = null;
|
|
|
|
if (mScriptManager.mContext.mVars.TryGetValue(varName, var value))
|
|
{
|
|
if (value.VariantType == typeof(String))
|
|
{
|
|
newString = scope:ReplaceBlock String(value.Get<String>());
|
|
}
|
|
}
|
|
|
|
if (newString == null)
|
|
{
|
|
if (mBuildContext != null)
|
|
{
|
|
if (mBuildContext.mScriptContext.mVars.TryGetValue(varName, var value))
|
|
{
|
|
if (value.VariantType == typeof(String))
|
|
{
|
|
newString = scope:ReplaceBlock String(value.Get<String>());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (newString != null)
|
|
{
|
|
result.Remove(i, parenPos - i + 1);
|
|
result.Insert(i, newString);
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
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))
|
|
{
|
|
OutputErrorLine("Invalid macro in {0}: {1}", errorContext, errorString);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public void GetWorkspaceBuildDir(String outResult)
|
|
{
|
|
if (mWorkspace.mDir == null)
|
|
return;
|
|
outResult.Append(mWorkspace.mDir, "/build/", mConfigName, "_", mPlatformName);
|
|
}
|
|
|
|
public void GetProjectBuildDir(Project project, String outResult)
|
|
{
|
|
GetWorkspaceBuildDir(outResult);
|
|
if (!outResult.IsEmpty)
|
|
outResult.Append("/", project.mProjectName);
|
|
}
|
|
|
|
public void GetClangOutputFilePathWithoutExtension(ProjectSource projectSource, String outResult)
|
|
{
|
|
GetProjectBuildDir(projectSource.mProject, outResult);
|
|
outResult.Append("/");
|
|
String fullPath = scope String();
|
|
projectSource.GetFullImportPath(fullPath);
|
|
Path.GetFileNameWithoutExtension(fullPath, outResult);
|
|
}
|
|
|
|
public void GetClangBuildString(Project project, Project.Options options, Workspace.Options workspaceOptions, bool isC, String clangOptions)
|
|
{
|
|
bool isClang = options.mCOptions.mCompilerType == Project.CCompilerType.Clang;
|
|
|
|
if (options.mCOptions.mEmitDebugInfo)
|
|
{
|
|
if (isClang)
|
|
{
|
|
clangOptions.Append("-g -fstandalone-debug ");
|
|
|
|
if (workspaceOptions.mToolsetType != .GNU)
|
|
clangOptions.Append("-gcodeview ");
|
|
|
|
}
|
|
else
|
|
clangOptions.Append("-g ");
|
|
}
|
|
|
|
//TODO:
|
|
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;
|
|
default:
|
|
}
|
|
|
|
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)
|
|
{
|
|
var fullIncludePath = scope String();
|
|
project.GetProjectFullPath(includePath, fullIncludePath);
|
|
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
|
|
{
|
|
clangOptions.Append("--target=");
|
|
if (TargetTriple.IsTargetTriple(gApp.mPlatformName))
|
|
clangOptions.Append(gApp.mPlatformName);
|
|
else
|
|
Workspace.PlatformType.GetTargetTripleByName(gApp.mPlatformName, workspaceOptions.mToolsetType, clangOptions);
|
|
clangOptions.Append(" ");
|
|
|
|
if (workspaceOptions.mToolsetType == .GNU)
|
|
{
|
|
//
|
|
}
|
|
else
|
|
{
|
|
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\" ");
|
|
}
|
|
}
|
|
|
|
if (options.mCOptions.mNoOmitFramePointers)
|
|
clangOptions.Append("-fno-omit-frame-pointer ");
|
|
|
|
if (options.mCOptions.mGenerateLLVMAsm)
|
|
clangOptions.Append("-emit-llvm -S ");
|
|
else
|
|
clangOptions.Append("-c ");
|
|
|
|
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, " ");
|
|
}
|
|
|
|
}
|
|
|
|
static void QuoteIfNeeded(String str)
|
|
{
|
|
if (!str.Contains(' '))
|
|
return;
|
|
|
|
for (int32 i = 0; i < str.Length; i++)
|
|
{
|
|
char8 c = str[i];
|
|
|
|
if ((c == '\\') || (c == '"'))
|
|
{
|
|
str.Insert(i, '\\');
|
|
i++;
|
|
}
|
|
}
|
|
|
|
str.Insert(0, '"');
|
|
str.Append('"');
|
|
}
|
|
|
|
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");
|
|
String clangCExePath = scope String(llvmDir, "bin/clang.exe");
|
|
String clangOptions = scope String(buildFileName);
|
|
QuoteIfNeeded(clangOptions);
|
|
clangOptions.Append(" ");
|
|
|
|
bool isC = false;
|
|
|
|
var ext = scope String();
|
|
Path.GetExtension(buildFileName, ext);
|
|
if (ext == ".c")
|
|
isC = true;
|
|
|
|
var buildStr = scope String();
|
|
GetClangBuildString(project, options, workspaceOptions, isC, buildStr);
|
|
clangOptions.Append(buildStr);
|
|
|
|
int lastDot = objName.LastIndexOf('.');
|
|
|
|
String depFileName = scope String(objName, 0, lastDot);
|
|
QuoteIfNeeded(depFileName);
|
|
depFileName.Append(".dep");
|
|
clangOptions.Append("-MD -MF ", depFileName, " ");
|
|
|
|
if (addOptions != null)
|
|
clangOptions.Append(addOptions, " ");
|
|
|
|
clangOptions.Append("-o ");
|
|
String quotedObjName = scope String(objName);
|
|
QuoteIfNeeded(quotedObjName);
|
|
clangOptions.Append(quotedObjName);
|
|
|
|
String compilerExePath = (options.mCOptions.mCompilerType == Project.CCompilerType.GCC) ? gccExePath : isC ? clangCExePath : clangCppExePath;
|
|
return QueueRun(compilerExePath, clangOptions, IDEApp.sApp.mInstallDir, .UTF8);
|
|
}
|
|
|
|
void SkipProjectCompile(Project project, Project hotProject)
|
|
{
|
|
Project.Options options = GetCurProjectOptions(project);
|
|
if (options == null)
|
|
return;
|
|
|
|
BfCompiler bfCompiler = mBfBuildCompiler;
|
|
var bfProject = mBfBuildSystem.mProjectMap[project];
|
|
bool bfHadOutputChanges;
|
|
List<String> bfFileNames = scope List<String>();
|
|
bfCompiler.GetOutputFileNames(bfProject, false, out bfHadOutputChanges, bfFileNames);
|
|
defer ClearAndDeleteItems(bfFileNames);
|
|
if (bfHadOutputChanges)
|
|
project.mNeedsTargetRebuild = true;
|
|
}
|
|
|
|
void GetTargetPaths(Project project, String platformName, Workspace.Options workspaceOptions, Project.Options options, List<String> outPaths)
|
|
{
|
|
String targetPath = scope String();
|
|
ResolveConfigString(platformName, workspaceOptions, project, options, "$(TargetPath)", "Target path", targetPath);
|
|
outPaths.Add(new String(targetPath));
|
|
|
|
#if BF_PLATFORM_WINDOWS
|
|
if ((project.mGeneralOptions.mTargetType == .BeefConsoleApplication) ||
|
|
(project.mGeneralOptions.mTargetType == .BeefGUIApplication))
|
|
{
|
|
if (workspaceOptions.mToolsetType != .GNU)
|
|
{
|
|
String pdbPath = scope String();
|
|
BuildContext.GetPdbPath(targetPath, workspaceOptions, options, pdbPath);
|
|
outPaths.Add(new String(pdbPath));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
public class ArgBuilder
|
|
{
|
|
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(' ');
|
|
}
|
|
|
|
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);*/
|
|
}
|
|
}
|
|
|
|
Project FindProject(String projectName)
|
|
{
|
|
return mWorkspace.FindProject(projectName);
|
|
}
|
|
|
|
public bool GetDependentProjectList(Project project, List<Project> orderedProjectList, List<Project> projectStack = null)
|
|
{
|
|
var useProjectStack = projectStack;
|
|
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)
|
|
{
|
|
OutputLine(StackStringFormat!("Unable to find project '{0}', a dependency of project '{1}'", dep.mProjectName, project.mProjectName));
|
|
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);
|
|
}
|
|
|
|
public virtual void LoadFailed()
|
|
{
|
|
if (mRunningTestScript)
|
|
mFailed = true;
|
|
}
|
|
|
|
public virtual void TestFailed()
|
|
{
|
|
mLastTestFailed = true;
|
|
}
|
|
|
|
protected virtual void CompileFailed()
|
|
{
|
|
if (mTestManager != null)
|
|
mTestManager.BuildFailed();
|
|
if (mVerbosity > .Quiet)
|
|
OutputLine("Compile failed.");
|
|
mLastCompileFailed = true;
|
|
|
|
if (mRunningTestScript)
|
|
{
|
|
if (!gApp.mScriptManager.mHadExpectingError)
|
|
gApp.mScriptManager.Fail("Compile failed");
|
|
}
|
|
else
|
|
{
|
|
Beep(MessageBeepType.Error);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ProcessBeefCompileResults(BfPassInstance passInstance, CompileKind compileKind, Project hotProject, Stopwatch startStopWatch)
|
|
{
|
|
bool didCompileSucceed = true;
|
|
if (passInstance != null)
|
|
{
|
|
if (mProfileCompileProfileId != 0)
|
|
mProfileCompileProfileId.Dispose();
|
|
|
|
while (true)
|
|
{
|
|
String str = scope String();
|
|
if (!passInstance.PopOutString(str))
|
|
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);
|
|
//OutputLine(str);
|
|
}
|
|
|
|
if ((passInstance.mFailed) && (passInstance.mCompileSucceeded))
|
|
{
|
|
// This can happen if we can't load a Beef file
|
|
CompileFailed();
|
|
passInstance.mCompileSucceeded = false;
|
|
}
|
|
|
|
didCompileSucceed = passInstance.mCompileSucceeded;
|
|
|
|
if (didCompileSucceed)
|
|
{
|
|
mLastCompileSucceeded = true;
|
|
mWorkspace.WithProjectItems(scope (item) =>
|
|
{
|
|
if (var projectSource = item as ProjectSource)
|
|
{
|
|
if (IsBeefFile(projectSource.mPath))
|
|
{
|
|
if (!projectSource.mHasChangedSinceLastCompile)
|
|
projectSource.mHasChangedSinceLastSuccessfulCompile = false;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
else
|
|
mLastCompileHadMessages = true;
|
|
|
|
delete passInstance;
|
|
}
|
|
|
|
if ((hotProject != null) && (passInstance != null) && (didCompileSucceed))
|
|
{
|
|
if (!mDebugger.mIsRunning)
|
|
{
|
|
OutputErrorLine("Hot compile failed - target no longer running");
|
|
CompileFailed();
|
|
return;
|
|
}
|
|
|
|
DebugManager.HotResolveFlags hotResolveFlags = .ActiveMethods;
|
|
|
|
if (mBfBuildCompiler.GetHasHotPendingDataChanges())
|
|
{
|
|
hotResolveFlags |= .Allocations;
|
|
mHotResolveState = .PendingWithDataChanges;
|
|
}
|
|
else
|
|
{
|
|
mHotResolveState = .Pending;
|
|
}
|
|
|
|
mHotResolveTryIdx = 0;
|
|
|
|
mDebugger.InitiateHotResolve(hotResolveFlags);
|
|
return;
|
|
}
|
|
|
|
List<Project> orderedProjectList = scope List<Project>();
|
|
GetOrderedProjectList(orderedProjectList);
|
|
if (!didCompileSucceed)
|
|
{
|
|
// Failed, bail out
|
|
for (var project in orderedProjectList)
|
|
{
|
|
SkipProjectCompile(project, hotProject);
|
|
}
|
|
CompileResult((hotProject != null) ? hotProject.mProjectName : null, false);
|
|
CompileFailed();
|
|
return;
|
|
}
|
|
|
|
var completedCompileCmd = new BuildCompletedCmd();
|
|
if (hotProject != null)
|
|
completedCompileCmd.mHotProjectName = new String(hotProject.mProjectName);
|
|
|
|
//TODO: Pass in
|
|
//Project project = mWorkspace.mProjects[0];
|
|
bool success = true;
|
|
List<String> hotFileNames = scope List<String>();
|
|
defer ClearAndDeleteItems(hotFileNames);
|
|
Debug.Assert(mBuildContext == null);
|
|
DeleteAndNullify!(mBuildContext);
|
|
mBuildContext = new .();
|
|
mBuildContext.mWorkspaceOptions = GetCurWorkspaceOptions();
|
|
|
|
for (var project in orderedProjectList)
|
|
{
|
|
if (!mBuildContext.QueueProjectCompile(project, hotProject, completedCompileCmd, hotFileNames, compileKind))
|
|
success = false;
|
|
}
|
|
|
|
if (hotFileNames.Count > 0)
|
|
{
|
|
// Why were we rehupping BEFORE hotLoad?
|
|
mDebugger.RehupBreakpoints(false, false);
|
|
|
|
String[] entries = scope String[hotFileNames.Count];
|
|
for (int32 i = 0; i < hotFileNames.Count; i++)
|
|
entries[i] = hotFileNames[i];
|
|
|
|
//
|
|
|
|
mBfBuildCompiler.HotCommit();
|
|
mDebugger.HotLoad(entries, mWorkspace.HotCompileIdx);
|
|
/*if (mDebugger.IsPaused())
|
|
PCChanged();*/
|
|
|
|
//mDebugger.RehupBreakpoints(false);
|
|
|
|
RefreshWatches();
|
|
}
|
|
|
|
if (!success)
|
|
CompileFailed();
|
|
|
|
if (success)
|
|
{
|
|
var options = GetCurWorkspaceOptions();
|
|
|
|
if (compileKind == .DebugAfter)
|
|
{
|
|
var startDebugCmd = new StartDebugCmd();
|
|
startDebugCmd.mWasCompiled = true;
|
|
startDebugCmd.mOnlyIfNotFailed = true;
|
|
startDebugCmd.mHotCompileEnabled = options.mAllowHotSwapping;
|
|
mExecutionQueue.Add(startDebugCmd);
|
|
}
|
|
else if (compileKind == .RunAfter)
|
|
{
|
|
StartupProject(false, true);
|
|
}
|
|
}
|
|
|
|
if (startStopWatch != null)
|
|
{
|
|
delete completedCompileCmd.mStopwatch;
|
|
var stopwatch = new Stopwatch();
|
|
stopwatch.CopyFrom(startStopWatch);
|
|
completedCompileCmd.mStopwatch = stopwatch;
|
|
}
|
|
mExecutionQueue.Add(completedCompileCmd);
|
|
}
|
|
|
|
bool CompileAndRun(bool debug)
|
|
{
|
|
if (AreTestsRunning())
|
|
return false;
|
|
if (IsCompiling)
|
|
return false;
|
|
|
|
if (!mExecutionQueue.IsEmpty)
|
|
{
|
|
if (var processCompileCmd = mExecutionQueue.Back as ProcessBfCompileCmd)
|
|
{
|
|
processCompileCmd.mCompileKind = debug ? .DebugAfter : .RunAfter;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
if (mInitialized)
|
|
DeleteAndNullify!(mLaunchData);
|
|
|
|
mOutputPanel.Clear();
|
|
OutputLine("Compiling...");
|
|
if (!Compile(debug ? .DebugAfter : .RunAfter))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
public void QueueProfiling(int threadId, String desc, int sampleRate)
|
|
{
|
|
var profileCmd = new ProfileCmd();
|
|
profileCmd.mThreadId = threadId;
|
|
profileCmd.mDesc = new String(desc);
|
|
profileCmd.mSampleRate = sampleRate;
|
|
if (var processCompileCmd = mExecutionQueue.Back as ProcessBfCompileCmd)
|
|
{
|
|
processCompileCmd.mProfileCmd = profileCmd;
|
|
}
|
|
}
|
|
|
|
[IDECommand]
|
|
void StopRunning()
|
|
{
|
|
if (AreTestsRunning())
|
|
{
|
|
mTestManager.Stop();
|
|
return;
|
|
}
|
|
|
|
if (mCrashDumpPath != null)
|
|
{
|
|
DeleteAndNullify!(mCrashDumpPath);
|
|
mDebugger.Detach();
|
|
mDebugger.mIsRunning = false;
|
|
mExecutionPaused = false;
|
|
}
|
|
|
|
if (mDebugger.mIsRunning)
|
|
{
|
|
mDebugger.StopDebugging();
|
|
}
|
|
}
|
|
|
|
public void AutoGenerateStartupCode(Project project)
|
|
{
|
|
// 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;
|
|
|
|
namespace {}
|
|
{{
|
|
class {}
|
|
{{
|
|
public static int Main(String[] args)
|
|
{{
|
|
return 0;
|
|
}}
|
|
}}
|
|
}}
|
|
""", namespaceName, className);
|
|
|
|
String srcPath = scope .();
|
|
project.mRootFolder.GetFullImportPath(srcPath);
|
|
Directory.CreateDirectory(srcPath).IgnoreError();
|
|
|
|
srcPath.Append(Path.DirectorySeparatorChar);
|
|
srcPath.Append("Program.bf");
|
|
|
|
if (!SafeWriteTextFile(srcPath, startupCode))
|
|
return;
|
|
|
|
OnWatchedFileChanged(project.mRootFolder, .FileCreated, srcPath);
|
|
|
|
if (project.IsEmpty)
|
|
return;
|
|
|
|
let projectSource = project.mRootFolder.mChildItems[0] as ProjectSource;
|
|
if (projectSource == null)
|
|
return;
|
|
|
|
#if !CLI
|
|
ShowSourceFile(srcPath);
|
|
#endif
|
|
}
|
|
|
|
protected bool Compile(CompileKind compileKind = .Normal, Project hotProject = null)
|
|
{
|
|
Debug.Assert(mBuildContext == null);
|
|
|
|
if (mWorkspace.mStartupProject != null)
|
|
{
|
|
if ((mWorkspace.mStartupProject.IsEmpty) && (!mWorkspace.IsDebugSession))
|
|
{
|
|
#if !CLI
|
|
DarkDialog dlg = new DarkDialog("Initialize Project?",
|
|
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());
|
|
#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;
|
|
}
|
|
}
|
|
|
|
let platform = Workspace.PlatformType.GetFromName(mPlatformName);
|
|
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;
|
|
}
|
|
|
|
canCompile = platform == hostPlatform;
|
|
switch (platform)
|
|
{
|
|
case .iOS:
|
|
canCompile = hostPlatform == .macOS;
|
|
case .Android:
|
|
canCompile = true;
|
|
case .Unknown:
|
|
canCompile = true;
|
|
case .Linux:
|
|
if (hostPlatform == .Windows)
|
|
canCompile = true; // Use WSL
|
|
default:
|
|
}
|
|
|
|
if (!canCompile)
|
|
{
|
|
OutputErrorLine("Cannot compile for platform '{}' from host platform '{}'", platform, hostPlatform);
|
|
return false;
|
|
}
|
|
|
|
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)
|
|
{
|
|
Fail(scope String()..AppendF("Failed to rename {0} to {1}", dbgBuildDir, tempDirName));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
String delDirName = new String(tempDirName);
|
|
ThreadPool.QueueUserWorkItem(new () =>
|
|
{
|
|
Utils.DelTree(delDirName).IgnoreError();
|
|
delete delDirName;
|
|
});
|
|
|
|
|
|
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);
|
|
|
|
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;
|
|
}
|
|
|
|
var workspaceOptions = GetCurWorkspaceOptions();
|
|
if ((compileKind == .RunAfter) || (compileKind == .DebugAfter))
|
|
{
|
|
if (workspaceOptions.mBuildKind == .Test)
|
|
{
|
|
OutputErrorLine("Cannot directly run Test workspace configurations. Use the 'Test' menu to run or debug tests.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (Workspace.PlatformType.GetFromName(mPlatformName) == .Windows)
|
|
{
|
|
if (!mSettings.mVSSettings.IsConfigured())
|
|
mSettings.mVSSettings.SetDefaults();
|
|
|
|
if (!mSettings.mVSSettings.IsConfigured())
|
|
{
|
|
String err =
|
|
"""
|
|
Beef requires the Microsoft C++ build tools for Visual Studio 2013 or later, but they don't seem to be installed.
|
|
|
|
Install just Microsoft Visual C++ Build Tools or the entire Visual Studio suite from:
|
|
https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2019
|
|
""";
|
|
|
|
#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();
|
|
psi.SetFileName("https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2019");
|
|
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());
|
|
MessageBeep(.Error);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (mProfileCompile)
|
|
mProfileCompileProfileId = Profiler.StartSampling("Compile");
|
|
|
|
if (mWantsBeefClean)
|
|
{
|
|
// We must finish cleaning before we can compile
|
|
while (mWantsBeefClean)
|
|
{
|
|
UpdateCompilersAndDebugger();
|
|
}
|
|
}
|
|
|
|
if ((!workspaceOptions.mIncrementalBuild) && (mCompileSinceCleanCount > 0) && (hotProject == null))
|
|
{
|
|
delete mBfBuildCompiler;
|
|
delete mBfBuildSystem;
|
|
mBfBuildSystem = new BfSystem();
|
|
mBfBuildCompiler = mBfBuildSystem.CreateCompiler(false);
|
|
|
|
for (var project in mWorkspace.mProjects)
|
|
{
|
|
mBfBuildSystem.AddProject(project);
|
|
}
|
|
}
|
|
|
|
bool lastCompileHadMessages = mLastCompileHadMessages;
|
|
mLastCompileFailed = false;
|
|
mLastCompileSucceeded = false;
|
|
mLastCompileHadMessages = false;
|
|
mCompileSinceCleanCount++;
|
|
ShowPanel(mOutputPanel, false);
|
|
|
|
if (mDisableBuilding)
|
|
{
|
|
OutputErrorLine("Compiling disabled!");
|
|
return false;
|
|
}
|
|
|
|
if (mExecutionQueue.Count > 0)
|
|
return false;
|
|
|
|
if ((mDebugger != null) && (mDebugger.mIsRunning) && (hotProject == null))
|
|
{
|
|
Debug.Assert(!mDebugger.mIsRunningCompiled);
|
|
Debug.Assert(compileKind == .Normal);
|
|
}
|
|
|
|
mHaveSourcesChangedExternallySinceLastCompile = false;
|
|
mHaveSourcesChangedInternallySinceLastCompile = false;
|
|
mWorkspace.SetupProjectCompileInstance(hotProject != null);
|
|
|
|
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);
|
|
|
|
SaveClangFiles();
|
|
|
|
if ((compileKind == .RunAfter) || (compileKind == .DebugAfter))
|
|
{
|
|
DeleteAndNullify!(mCompileAndRunStopwatch);
|
|
mCompileAndRunStopwatch = new Stopwatch();
|
|
mCompileAndRunStopwatch.Start();
|
|
}
|
|
|
|
//mBfBuildCompiler.QueueStartTiming();
|
|
mBfBuildCompiler.ClearCompletionPercentage();
|
|
|
|
BfPassInstance passInstance = null;
|
|
|
|
passInstance = CompileBeef(hotProject, hotIdx, lastCompileHadMessages, let hadBeef);
|
|
if (passInstance == null)
|
|
{
|
|
CompileFailed();
|
|
return false;
|
|
}
|
|
|
|
ProcessBfCompileCmd processCompileCmd = new ProcessBfCompileCmd();
|
|
processCompileCmd.mBfPassInstance = passInstance;
|
|
processCompileCmd.mCompileKind = compileKind;
|
|
processCompileCmd.mHotProject = hotProject;
|
|
processCompileCmd.mStopwatch = new Stopwatch();
|
|
processCompileCmd.mStopwatch.Start();
|
|
processCompileCmd.mHadBeef = hadBeef;
|
|
mExecutionQueue.Add(processCompileCmd);
|
|
|
|
return true;
|
|
}
|
|
|
|
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"));
|
|
}
|
|
|
|
bool StartupProject(bool doDebug, bool wasCompiled)
|
|
{
|
|
mProfilePanel.Clear();
|
|
|
|
if (mWorkspace.mStartupProject == null)
|
|
{
|
|
OutputErrorLine("No startup project started");
|
|
return false;
|
|
}
|
|
var project = mWorkspace.mStartupProject;
|
|
var workspaceOptions = GetCurWorkspaceOptions();
|
|
var options = GetCurProjectOptions(project);
|
|
if (options == null)
|
|
{
|
|
OutputErrorLine("Startup project '{0}' not enabled", mWorkspace.mStartupProject.mProjectName);
|
|
return false;
|
|
}
|
|
|
|
mDebugger.ClearInvalidBreakpoints();
|
|
|
|
mTargetDidInitBreak = false;
|
|
mTargetHadFirstBreak = false;
|
|
|
|
//options.mDebugOptions.mCommand
|
|
|
|
String launchPath = scope String();
|
|
ResolveConfigString(mPlatformName, workspaceOptions, project, options, options.mDebugOptions.mCommand, "debug command", launchPath);
|
|
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);
|
|
var workingDir = scope String();
|
|
Path.GetAbsolutePath(workingDirRel, project.mProjectDir, workingDir);
|
|
|
|
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;
|
|
}
|
|
|
|
if (workingDir.IsEmpty)
|
|
Path.GetDirectoryPath(launchPath, workingDir).IgnoreError();
|
|
|
|
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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
if (!mDebugger.OpenFile(launchPath, targetPath, arguments, workingDir, envBlock, wasCompiled, workspaceOptions.mAllowHotSwapping))
|
|
{
|
|
DeleteAndNullify!(mCompileAndRunStopwatch);
|
|
return false;
|
|
}
|
|
|
|
CheckDebugVisualizers();
|
|
|
|
mDebugger.mIsRunning = true;
|
|
WithSourceViewPanels(scope (sourceView) =>
|
|
{
|
|
sourceView.RehupAlias();
|
|
});
|
|
|
|
mDebugger.RehupBreakpoints(true);
|
|
mDebugger.Run();
|
|
mModulePanel.ModulesChanged();
|
|
|
|
if (mCompileAndRunStopwatch != null)
|
|
{
|
|
mCompileAndRunStopwatch.Stop();
|
|
OutputLine("Compile-to-debug time: {0:0.00}s", mCompileAndRunStopwatch.ElapsedMilliseconds / 1000.0f);
|
|
}
|
|
|
|
if ((mTargetStartWithStep) && (mMainBreakpoint == null))
|
|
{
|
|
// The idea is that we don't want to step into static initializers, so we
|
|
// temporarily break on _main and then we single step
|
|
//mMainBreakpoint = mDebugger.CreateSymbolBreakpoint("_ZN3Hey4Dude3Bro9TestClass4MainEv");
|
|
if ((project.mGeneralOptions.mTargetType == Project.TargetType.BeefConsoleApplication) ||
|
|
(project.mGeneralOptions.mTargetType == Project.TargetType.BeefGUIApplication))
|
|
mMainBreakpoint = mDebugger.CreateSymbolBreakpoint("-BeefStartProgram");
|
|
else
|
|
{
|
|
mMainBreakpoint = mDebugger.CreateSymbolBreakpoint("-main");
|
|
#if BF_PLATFORM_WINDOWS
|
|
//mMainBreakpoint2 = mDebugger.CreateSymbolBreakpoint("-WinMain");
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
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.RehupBreakpoints(true);
|
|
mDebugger.Run();
|
|
mIsAttachPendingSourceShow = true;
|
|
}
|
|
|
|
void ShowPanel(Widget panel, bool setFocus = true)
|
|
{
|
|
WithTabs(scope (tab) =>
|
|
{
|
|
if (tab.mContent == panel)
|
|
tab.Activate(setFocus);
|
|
});
|
|
}
|
|
|
|
void ShowStartupFile()
|
|
{
|
|
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;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
public void CreateDefaultLayout()
|
|
{
|
|
//TODO:
|
|
//mConfigName.Set("Dbg");
|
|
|
|
if ((!mRunningTestScript) && (!mIsFirstRun) && (LoadDefaultLayoutData()))
|
|
{
|
|
return;
|
|
}
|
|
|
|
bool docOnlyView = false;
|
|
|
|
TabbedView projectTabbedView = CreateTabbedView();
|
|
SetupTab(projectTabbedView, "Workspace", 0, mProjectPanel, false);
|
|
projectTabbedView.SetRequestedSize(GS!(200), GS!(200));
|
|
projectTabbedView.mWidth = GS!(200);
|
|
|
|
//TabbedView propertiesView = CreateTabbedView();
|
|
//propertiesView.AddTab("Properties", 0, mPropertiesPanel, false);
|
|
//propertiesView.SetRequestedSize(250, 250);
|
|
|
|
if (!docOnlyView)
|
|
{
|
|
mDockingFrame.AddDockedWidget(projectTabbedView, null, DockingFrame.WidgetAlign.Left);
|
|
EnsureDocumentArea();
|
|
//mDockingFrame.AddDockedWidget(propertiesView, null, DockingFrame.WidgetAlign.Right);
|
|
}
|
|
|
|
var outputTabbedView = CreateTabbedView();
|
|
mDockingFrame.AddDockedWidget(outputTabbedView, null, DockingFrame.WidgetAlign.Bottom);
|
|
outputTabbedView.SetRequestedSize(GS!(250), GS!(250));
|
|
|
|
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()
|
|
{
|
|
scope AutoBeefPerf("IDEApp.CreateBfSystems");
|
|
|
|
#if !CLI
|
|
if (!mNoResolve)
|
|
{
|
|
mBfResolveSystem = new BfSystem();
|
|
mBfResolveCompiler = mBfResolveSystem.CreateCompiler(true);
|
|
mBfResolveHelper = new BfResolveHelper();
|
|
}
|
|
#if IDE_C_SUPPORT
|
|
mResolveClang = new ClangCompiler(true);
|
|
#endif
|
|
#endif
|
|
|
|
mCompileSinceCleanCount = 0;
|
|
mBfBuildSystem = new BfSystem();
|
|
mBfBuildCompiler = mBfBuildSystem.CreateCompiler(false);
|
|
#if IDE_C_SUPPORT
|
|
mDepClang = new ClangCompiler(false);
|
|
mDepClang.mPairedCompiler = mResolveClang;
|
|
mResolveClang.mPairedCompiler = mDepClang;
|
|
#endif
|
|
}
|
|
|
|
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");
|
|
|
|
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);
|
|
|
|
mMainWindow.SetTitle(title);
|
|
}
|
|
|
|
public void CreateSpellChecker()
|
|
{
|
|
mSpellChecker = new SpellChecker();
|
|
if (mSpellChecker.Init(scope String(mInstallDir, "en_US")) case .Err)
|
|
{
|
|
DeleteAndNullify!(mSpellChecker);
|
|
}
|
|
}
|
|
|
|
public FileVersionInfo GetVersionInfo(out DateTime exeTime)
|
|
{
|
|
exeTime = default;
|
|
|
|
if (mVersionInfo == null)
|
|
{
|
|
String exeFilePath = scope .();
|
|
Environment.GetExecutableFilePath(exeFilePath);
|
|
mVersionInfo = new .();
|
|
mVersionInfo.GetVersionInfo(exeFilePath).IgnoreError();
|
|
if (!String.IsNullOrEmpty(mVersionInfo.FileVersion))
|
|
Debug.Assert(mVersionInfo.FileVersion.StartsWith(cVersion));
|
|
#if BF_PLATFORM_WINDOWS
|
|
exeTime = File.GetLastWriteTime(exeFilePath).GetValueOrDefault();
|
|
#endif
|
|
}
|
|
return mVersionInfo;
|
|
}
|
|
|
|
#if !CLI
|
|
public override void Init()
|
|
{
|
|
scope AutoBeefPerf("IDEApp.Init");
|
|
|
|
//int zag = 123;
|
|
|
|
if (mVerbosity == .Default)
|
|
mVerbosity = .Detailed;
|
|
|
|
mStartedWithTestScript = mRunningTestScript;
|
|
|
|
mCommands.Init();
|
|
EnableGCCollect = mEnableGCCollect;
|
|
|
|
if (mConfigName.IsEmpty)
|
|
mConfigName.Set("Debug");
|
|
if (mPlatformName.IsEmpty)
|
|
mPlatformName.Set(sPlatform64Name ?? sPlatform32Name);
|
|
|
|
Directory.GetCurrentDirectory(mInitialCWD);
|
|
|
|
#if DEBUG
|
|
//mColorMatrix = Matrix4.Identity;
|
|
#endif
|
|
|
|
base.Init();
|
|
mSettings.Apply();
|
|
|
|
//Yoop();
|
|
|
|
/*for (int i = 0; i < 100*1024*1024; i++)
|
|
{
|
|
mTestString.Append("Hey, this is a test string!");
|
|
}*/
|
|
|
|
mAutoDirty = false;
|
|
WidgetWindow.sOnWindowClosed.Add(new => HandleWindowClosed);
|
|
|
|
//mProjectDir = BFApp.mApp.mInstallDir + "../../";
|
|
|
|
if (!mRunningTestScript)
|
|
{
|
|
// User setting can affect automated testing, so use default settings
|
|
if (!mIsFirstRun)
|
|
mSettings.Load();
|
|
mSettings.Apply();
|
|
mIsFirstRun = !mSettings.mLoadedSettings;
|
|
#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
|
|
}
|
|
|
|
Font.AddFontFailEntry("Segoe UI", scope String()..AppendF("{}fonts/NotoSans-Regular.ttf", mInstallDir));
|
|
|
|
DarkTheme aTheme = new DarkTheme();
|
|
aTheme.Init();
|
|
ThemeFactory.mDefault = aTheme;
|
|
|
|
mTinyCodeFont = new Font();
|
|
mCodeFont = new Font();
|
|
|
|
//mCodeFont = Font.LoadFromFile(BFApp.sApp.mInstallDir + "fonts/SourceCodePro32.fnt");
|
|
|
|
//mCodeFont = Font.LoadFromFile(BFApp.sApp.mInstallDir + "fonts/SegoeUI24.fnt");
|
|
|
|
mMainFrame = new MainFrame();
|
|
mDockingFrame = mMainFrame.mDockingFrame;
|
|
|
|
mSquiggleImage = Image.LoadFromFile(scope String(mInstallDir, "images/Squiggle.png"));
|
|
mCircleImage = Image.LoadFromFile(scope String(mInstallDir, "images/Circle.png"));
|
|
|
|
RehupScale();
|
|
|
|
CreateBfSystems();
|
|
if (mWantsClean)
|
|
{
|
|
mBfBuildCompiler.ClearBuildCache();
|
|
mWantsClean = false;
|
|
}
|
|
|
|
mProjectPanel = new ProjectPanel();
|
|
mProjectPanel.mAutoDelete = false;
|
|
mClassViewPanel = new ClassViewPanel();
|
|
mClassViewPanel.mAutoDelete = false;
|
|
mOutputPanel = new OutputPanel(true);
|
|
mOutputPanel.mAutoDelete = false;
|
|
mImmediatePanel = new ImmediatePanel();
|
|
mImmediatePanel.mAutoDelete = false;
|
|
mFindResultsPanel = new FindResultsPanel();
|
|
mFindResultsPanel.mAutoDelete = false;
|
|
mAutoWatchPanel = new WatchPanel(true);
|
|
mAutoWatchPanel.mAutoDelete = false;
|
|
mWatchPanel = new WatchPanel(false);
|
|
mWatchPanel.mAutoDelete = false;
|
|
mMemoryPanel = new MemoryPanel();
|
|
mMemoryPanel.mAutoDelete = false;
|
|
mCallStackPanel = new CallStackPanel();
|
|
mCallStackPanel.mAutoDelete = false;
|
|
mBreakpointPanel = new BreakpointPanel();
|
|
mBreakpointPanel.mAutoDelete = false;
|
|
mErrorsPanel = new ErrorsPanel();
|
|
mErrorsPanel.mAutoDelete = false;
|
|
mModulePanel = new ModulePanel();
|
|
mModulePanel.mAutoDelete = false;
|
|
mThreadPanel = new ThreadPanel();
|
|
mThreadPanel.mAutoDelete = false;
|
|
mProfilePanel = new ProfilePanel();
|
|
mProfilePanel.mAutoDelete = false;
|
|
mPropertiesPanel = new PropertiesPanel();
|
|
mPropertiesPanel.mAutoDelete = false;
|
|
mAutoCompletePanel = new AutoCompletePanel();
|
|
mAutoCompletePanel.mAutoDelete = false;
|
|
|
|
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);
|
|
|
|
/*if (!mRunningTestScript)
|
|
{
|
|
LoadUserSettings(); // User setting can affect automated testing, so use default settings
|
|
mSettings.Load();
|
|
mSettings.Apply();
|
|
}*/
|
|
|
|
if (mWorkspace.mDir != null)
|
|
{
|
|
mWorkspace.mName = new String();
|
|
Path.GetFileName(mWorkspace.mDir, mWorkspace.mName);
|
|
LoadWorkspace(mVerb);
|
|
}
|
|
else if (mWorkspace.IsSingleFileWorkspace)
|
|
{
|
|
mWorkspace.mName = new String();
|
|
Path.GetFileNameWithoutExtension(mWorkspace.mCompositeFile.mFilePath, mWorkspace.mName);
|
|
LoadWorkspace(mVerb);
|
|
}
|
|
|
|
bool loadedWorkspaceUserData = false;
|
|
|
|
if ((!mRunningTestScript) && (LoadWorkspaceUserData()))
|
|
{
|
|
loadedWorkspaceUserData = true;
|
|
}
|
|
|
|
if (mSetConfigName != null)
|
|
{
|
|
mConfigName.Set(mSetConfigName);
|
|
DeleteAndNullify!(mSetConfigName);
|
|
}
|
|
|
|
if (mSetPlatformName != null)
|
|
{
|
|
mPlatformName.Set(mSetPlatformName);
|
|
DeleteAndNullify!(mSetPlatformName);
|
|
}
|
|
|
|
WorkspaceLoaded();
|
|
|
|
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));
|
|
|
|
mRequestedWindowRect = .(workX + (workWidth - width)/2, workY + (workHeight - height)/2, width, height);
|
|
}
|
|
|
|
//
|
|
{
|
|
BFWindow.Flags flags = .Border | .ThickFrame | .Resizable | .SysMenu |
|
|
.Caption | .Minimize | .Maximize | .QuitOnClose | .Menu | .PopupPosition;
|
|
if (mRunningTestScript)
|
|
flags |= .NoActivate;
|
|
|
|
if (mRequestedShowKind == .Maximized)
|
|
flags |= .ShowMaximized;
|
|
|
|
scope AutoBeefPerf("IDEApp.Init:CreateMainWindow");
|
|
mMainWindow = new WidgetWindow(null, "Beef IDE", (int32)mRequestedWindowRect.mX,
|
|
(int32)mRequestedWindowRect.mY, (int32)mRequestedWindowRect.mWidth, (int32)mRequestedWindowRect.mHeight,
|
|
flags, mMainFrame);
|
|
}
|
|
|
|
if (mIsFirstRun)
|
|
{
|
|
// If this is our first time running, set up a scale based on DPI
|
|
int dpi = mMainWindow.GetDPI();
|
|
if (dpi >= 120)
|
|
{
|
|
mSettings.mEditorSettings.mUIScale = 100 * Math.Min(dpi / 96.0f, 4.0f);
|
|
mSettings.Apply();
|
|
}
|
|
}
|
|
|
|
if (!loadedWorkspaceUserData)
|
|
CreateDefaultLayout();
|
|
|
|
UpdateTitle();
|
|
mMainWindow.SetMinimumSize(GS!(480), GS!(360));
|
|
mMainWindow.mIsMainWindow = true;
|
|
mMainWindow.mOnWindowKeyDown.Add(new => SysKeyDown);
|
|
mMainWindow.mOnWindowCloseQuery.Add(new => AllowClose);
|
|
CreateMenu();
|
|
UpdateRecentDisplayedFilesMenuItems();
|
|
if (mRecentlyDisplayedFiles.Count > 0)
|
|
ShowRecentFile(0);
|
|
|
|
mProjectPanel.RebuildUI();
|
|
|
|
if (mProcessAttachId != 0)
|
|
{
|
|
Process debugProcess = scope Process();
|
|
switch (debugProcess.GetProcessById(mProcessAttachId))
|
|
{
|
|
case .Ok:
|
|
case .Err:
|
|
Fail(StackStringFormat!("Unable to locate process id {0}", mProcessAttachId));
|
|
}
|
|
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
|
|
|
|
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;
|
|
mExecutionPaused = false; // Make this false so we can detect a Pause immediately
|
|
mIsAttachPendingSourceShow = true;
|
|
}
|
|
else
|
|
{
|
|
Fail(StackStringFormat!("Failed to load minidump '{0}'", mCrashDumpPath));
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
|
|
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();
|
|
|
|
if ((mIsFirstRun) && (!mWorkspace.IsInitialized))
|
|
ShowWelcome();
|
|
}
|
|
#endif
|
|
void ShowWelcome()
|
|
{
|
|
WelcomePanel welcomePanel = new .();
|
|
TabbedView tabbedView = GetDefaultDocumentTabbedView();
|
|
let tabButton = SetupTab(tabbedView, "Welcome", 0, welcomePanel, true);
|
|
tabButton.Activate();
|
|
}
|
|
|
|
void LoadConfig()
|
|
{
|
|
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;
|
|
float tinyFontSize = fontSize * 8.0f/9.0f;
|
|
|
|
String err = scope String();
|
|
void FontFail(StringView name)
|
|
{
|
|
if (!err.IsEmpty)
|
|
err.Append("\n");
|
|
err.AppendF("Failed to load font '{}'", name);
|
|
}
|
|
|
|
bool isFirstFont = true;
|
|
for (var fontName in mSettings.mEditorSettings.mFonts)
|
|
FontLoop:
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
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();
|
|
mCodeFont.AddAlternate("Segoe UI Historic", fontSize).IgnoreError();
|
|
mCodeFont.AddAlternate("Segoe UI Emoji", fontSize).IgnoreError();
|
|
|
|
mTinyCodeFont.AddAlternate("Segoe UI", tinyFontSize);
|
|
mTinyCodeFont.AddAlternate("Segoe UI Symbol", tinyFontSize).IgnoreError();
|
|
mTinyCodeFont.AddAlternate("Segoe UI Historic", tinyFontSize).IgnoreError();
|
|
mTinyCodeFont.AddAlternate("Segoe UI Emoji", tinyFontSize).IgnoreError();
|
|
|
|
/*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);*/
|
|
}
|
|
|
|
if (!err.IsEmpty)
|
|
{
|
|
OutputErrorLine(err);
|
|
Fail(err);
|
|
}
|
|
|
|
//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);
|
|
|
|
mMainWindow?.SetMinimumSize(GS!(480), GS!(360));
|
|
|
|
/*for (var window in gApp.mWindows)
|
|
{
|
|
|
|
window.SetMinimumSize(GS!());
|
|
}*/
|
|
}
|
|
|
|
void HandleWindowClosed(BFWindow window)
|
|
{
|
|
if (mWindowDatas.GetAndRemove(window) case .Ok((?, var windowData)))
|
|
{
|
|
delete windowData;
|
|
}
|
|
}
|
|
|
|
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()
|
|
{
|
|
}
|
|
|
|
public enum EvalResult
|
|
{
|
|
Normal,
|
|
Pending
|
|
}
|
|
|
|
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)
|
|
{
|
|
defer
|
|
{
|
|
if (mPendingDebugExprHandler != pendingHandler)
|
|
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;
|
|
mDebugger.GetStackMethodOwner(mDebugger.mActiveCallStackIdx, curMethodOwner, out stackLanguage);
|
|
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;
|
|
}
|
|
|
|
void DebuggerPaused()
|
|
{
|
|
mDebugger.mActiveCallStackIdx = 0;
|
|
mExecutionPaused = true;
|
|
mDebugger.GetRunState();
|
|
WithWatchPanels(scope (watchPanel) =>
|
|
{
|
|
watchPanel.SetDisabled(false);
|
|
});
|
|
mMemoryPanel.SetDisabled(false);
|
|
mCallStackPanel.SetDisabled(false);
|
|
}
|
|
|
|
void WithWatchPanels(delegate void(WatchPanel watchPanel) dlg)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DebuggerUnpaused()
|
|
{
|
|
// Leave target in background for a bit, incase we immediately
|
|
// hit another breakpoint
|
|
mDebugger.mActiveCallStackIdx = 0;
|
|
mForegroundTargetCountdown = 30;
|
|
mExecutionPaused = false;
|
|
WithWatchPanels(scope (watchPanel) =>
|
|
{
|
|
watchPanel.SetDisabled(true);
|
|
});
|
|
mMemoryPanel.SetDisabled(true);
|
|
mCallStackPanel.SetDisabled(true);
|
|
|
|
// If hoverwatch is up, we need to close it so we refresh the value
|
|
var sourceViewPanel = GetActiveSourceViewPanel();
|
|
if ((sourceViewPanel != null) && (sourceViewPanel.mHoverWatch != null))
|
|
{
|
|
sourceViewPanel.mHoverWatch.Close();
|
|
}
|
|
mDebuggerContinueIdx++;
|
|
}
|
|
|
|
public void StepIntoSpecific(int addr)
|
|
{
|
|
if (mExecutionPaused)
|
|
{
|
|
DebuggerUnpaused();
|
|
mDebugger.StepIntoSpecific(addr);
|
|
}
|
|
}
|
|
|
|
void FinishPendingHotResolve()
|
|
{
|
|
List<uint8> typeData = scope .();
|
|
String stackListStr = scope .();
|
|
|
|
if (!mDebugger.GetHotResolveData(typeData, stackListStr))
|
|
return;
|
|
|
|
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);
|
|
}
|
|
else
|
|
{
|
|
if (mHotResolveTryIdx == 0)
|
|
{
|
|
OutputErrorLine("ERROR: Hot type data changes cannot be applied because of the following types");
|
|
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) =>
|
|
{
|
|
var projectSource = projectItem as ProjectSource;
|
|
if (projectSource != null)
|
|
{
|
|
if (projectSource.mIncludeKind == .Ignore)
|
|
return;
|
|
var resolveCompiler = GetProjectCompilerForFile(projectSource.mPath);
|
|
if (resolveCompiler == mBfResolveCompiler)
|
|
resolveCompiler.QueueProjectSource(projectSource, .None, false);
|
|
projectSource.mHasChangedSinceLastCompile = true;
|
|
}
|
|
});
|
|
}
|
|
|
|
void RemoveProjectItems(Project project)
|
|
{
|
|
project.WithProjectItems(scope (projectItem) =>
|
|
{
|
|
var projectSource = projectItem as ProjectSource;
|
|
if (projectSource != null)
|
|
{
|
|
var resolveCompiler = GetProjectCompilerForFile(projectSource.mPath);
|
|
if ((resolveCompiler == mBfResolveCompiler) && (resolveCompiler != null))
|
|
resolveCompiler.QueueProjectSourceRemoved(projectSource);
|
|
|
|
if (IsBeefFile(projectSource.mPath))
|
|
{
|
|
mBfBuildCompiler.QueueProjectSourceRemoved(projectSource);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
void UpdateCompilersAndDebugger()
|
|
{
|
|
scope AutoBeefPerf("UpdateCompilersAndDebugger");
|
|
|
|
var msg = scope String();
|
|
|
|
while (true)
|
|
{
|
|
DebugManager.RunState runState = .NotStarted;
|
|
|
|
if (mDebugger != null)
|
|
{
|
|
mDebugger.Update();
|
|
|
|
runState = mDebugger.GetRunState();
|
|
mDebuggerPerformingTask = (runState == .DebugEval) || (runState == .DebugEval_Done) || (runState == .SearchingSymSrv);
|
|
|
|
if (mDebugAutoRun)
|
|
{
|
|
if (runState == DebugManager.RunState.NotStarted)
|
|
{
|
|
mOutputPanel.Clear();
|
|
|
|
if ((mExecutionQueue.Count == 0) && (!mDebugger.mIsRunning))
|
|
{
|
|
mTargetStartWithStep = true;
|
|
var startDbgCmd = new StartDebugCmd();
|
|
startDbgCmd.mWasCompiled = true;
|
|
startDbgCmd.mOnlyIfNotFailed = true;
|
|
mExecutionQueue.Add(startDbgCmd);
|
|
}
|
|
}
|
|
else if (runState == DebugManager.RunState.Paused)
|
|
{
|
|
mDebugger.Continue();
|
|
//mDebugger.Terminate();
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
if (mDebugAutoBuild)
|
|
{
|
|
if ((!mBfBuildCompiler.HasQueuedCommands()) && (mExecutionInstances.Count == 0) && (mExecutionQueue.Count == 0))
|
|
{
|
|
OutputLine("Autobuilding...");
|
|
CompileBeef(null, 0, mLastCompileSucceeded, let hadBeef);
|
|
}
|
|
}
|
|
|
|
bool didClean = false;
|
|
if (mWantsBeefClean)
|
|
{
|
|
if ((!mBfBuildCompiler.HasQueuedCommands()) &&
|
|
((mBfResolveCompiler == null) || (!mBfResolveCompiler.HasQueuedCommands())))
|
|
{
|
|
didClean = true;
|
|
OutputLine("Cleaned Beef.");
|
|
|
|
if (mErrorsPanel != null)
|
|
mErrorsPanel.ClearParserErrors(null);
|
|
|
|
delete mBfResolveCompiler;
|
|
delete mBfResolveSystem;
|
|
delete mBfResolveHelper;
|
|
delete mBfBuildCompiler;
|
|
delete mBfBuildSystem;
|
|
|
|
///
|
|
mDebugger.FullReportMemory();
|
|
|
|
var workspaceBuildDir = scope String();
|
|
GetWorkspaceBuildDir(workspaceBuildDir);
|
|
if (Directory.Exists(workspaceBuildDir))
|
|
{
|
|
var result = Utils.DelTree(workspaceBuildDir, scope (fileName) =>
|
|
{
|
|
return fileName.EndsWith("build.dat");
|
|
});
|
|
if (result case .Err)
|
|
{
|
|
var str = scope String("Failed to delete folder: ", workspaceBuildDir);
|
|
Fail(str);
|
|
}
|
|
}
|
|
|
|
//ResolveConfigString(project, options, "$(TargetPath)", error, targetPath);
|
|
|
|
if (!mNoResolve)
|
|
{
|
|
mBfResolveSystem = new BfSystem();
|
|
mBfResolveCompiler = mBfResolveSystem.CreateCompiler(true);
|
|
mBfResolveHelper = new BfResolveHelper();
|
|
}
|
|
|
|
mCompileSinceCleanCount = 0;
|
|
mBfBuildSystem = new BfSystem();
|
|
mBfBuildCompiler = mBfBuildSystem.CreateCompiler(false);
|
|
mBfBuildCompiler.ClearBuildCache();
|
|
|
|
/*foreach (var project in mWorkspace.mProjects)
|
|
{
|
|
mBfBuildSystem.AddProject(project);
|
|
if (mBfResolveSystem != null)
|
|
mBfResolveSystem.AddProject(project);
|
|
}
|
|
|
|
foreach (var project in mWorkspace.mProjects)
|
|
{
|
|
project.WithProjectItems(scope (projectItem) =>
|
|
{
|
|
var projectSource = projectItem as ProjectSource;
|
|
if (projectSource != null)
|
|
{
|
|
var resolveCompiler = GetProjectCompilerForFile(projectSource.mPath);
|
|
if (resolveCompiler == mBfResolveCompiler)
|
|
resolveCompiler.QueueProjectSource(projectSource);
|
|
}
|
|
});
|
|
}
|
|
mBfResolveCompiler.QueueDeferredResolveAll();
|
|
CurrentWorkspaceConfigChanged();*/
|
|
|
|
mWantsBeefClean = false;
|
|
}
|
|
}
|
|
|
|
if (mWantsClean)
|
|
{
|
|
if ((!mBfBuildCompiler.HasQueuedCommands()) && (!mBfResolveCompiler.HasQueuedCommands())
|
|
#if IDE_C_SUPPORT
|
|
&& (!mDepClang.HasQueuedCommands()) && (!mResolveClang.HasQueuedCommands())
|
|
#endif
|
|
)
|
|
{
|
|
OutputLine("Cleaned.");
|
|
|
|
if (mErrorsPanel != null)
|
|
mErrorsPanel.ClearParserErrors(null);
|
|
|
|
let workspaceOptions = GetCurWorkspaceOptions();
|
|
|
|
delete mBfResolveHelper;
|
|
delete mBfResolveCompiler;
|
|
delete mBfResolveSystem;
|
|
#if IDE_C_SUPPORT
|
|
delete mResolveClang;
|
|
#endif
|
|
|
|
delete mBfBuildCompiler;
|
|
delete mBfBuildSystem;
|
|
#if IDE_C_SUPPORT
|
|
delete mDepClang;
|
|
#endif
|
|
//
|
|
|
|
CreateBfSystems();
|
|
mBfBuildCompiler.ClearBuildCache();
|
|
|
|
var workspaceBuildDir = scope String();
|
|
GetWorkspaceBuildDir(workspaceBuildDir);
|
|
if (Directory.Exists(workspaceBuildDir))
|
|
{
|
|
var result = Utils.DelTree(workspaceBuildDir);
|
|
if (result case .Err)
|
|
{
|
|
var str = scope String("Failed to delete folder: ", workspaceBuildDir);
|
|
//result.Exception.ToString(str);
|
|
Fail(str);
|
|
//result.Dispose();
|
|
}
|
|
}
|
|
|
|
List<String> deleteFails = scope .();
|
|
|
|
for (var project in mWorkspace.mProjects)
|
|
{
|
|
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;
|
|
|
|
// 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);
|
|
|
|
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;
|
|
mWantsClean = false;
|
|
mDbgCompileIdx = -1;
|
|
mDbgHighestTime = default;
|
|
}
|
|
}
|
|
|
|
if (didClean)
|
|
{
|
|
for (var project in mWorkspace.mProjects)
|
|
{
|
|
mBfBuildSystem.AddProject(project);
|
|
if (mBfResolveSystem != null)
|
|
mBfResolveSystem.AddProject(project);
|
|
}
|
|
|
|
if (mBfResolveSystem != null)
|
|
{
|
|
PreConfigureBeefSystem(mBfResolveSystem, mBfResolveCompiler);
|
|
for (var project in mWorkspace.mProjects)
|
|
{
|
|
SetupBeefProjectSettings(mBfResolveSystem, mBfResolveCompiler, project);
|
|
}
|
|
}
|
|
|
|
CurrentWorkspaceConfigChanged();
|
|
|
|
if (mBfResolveSystem != null)
|
|
{
|
|
/*for (var project in mWorkspace.mProjects)
|
|
{
|
|
if (IsProjectEnabled(project))
|
|
QueueProjectItems(project);
|
|
}*/
|
|
mBfResolveCompiler.QueueDeferredResolveAll();
|
|
}
|
|
}
|
|
|
|
mBfBuildSystem.Update();
|
|
mBfBuildCompiler.Update();
|
|
while (true)
|
|
{
|
|
#if CLI
|
|
if (mCompilingBeef)
|
|
break;
|
|
#endif
|
|
|
|
msg.Clear();
|
|
if (!mBfBuildCompiler.PopMessage(msg))
|
|
break;
|
|
OutputLineSmart(msg);
|
|
}
|
|
|
|
if (mBfResolveSystem != null)
|
|
{
|
|
mBfResolveSystem.Update();
|
|
mBfResolveCompiler.Update();
|
|
mBfResolveCompiler.ClearMessages();
|
|
mBfResolveHelper.Update();
|
|
}
|
|
|
|
#if IDE_C_SUPPORT
|
|
mDepClang.Update();
|
|
if ((!IsCompiling) && (!mDepClang.IsPerformingBackgroundOperation()))
|
|
{
|
|
// Only leave mCompileWaitsForQueueEmpty false for a fast initial build
|
|
mDepClang.mCompileWaitsForQueueEmpty = true;
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
msg.Clear();
|
|
if (!mDepClang.PopMessage(msg))
|
|
break;
|
|
OutputLine(msg);
|
|
}
|
|
|
|
mResolveClang.Update();
|
|
mResolveClang.ClearMessages();
|
|
#endif
|
|
|
|
bool wantDebuggerDetach = false;
|
|
if ((mDebugger != null) && (runState == DebugManager.RunState.Terminated))
|
|
{
|
|
wantDebuggerDetach = true;
|
|
}
|
|
|
|
String deferredMsgType = scope String();
|
|
String deferredOutput = scope String();
|
|
|
|
bool hadMessages = false;
|
|
while (mDebugger != null)
|
|
{
|
|
msg.Clear();
|
|
if (!mDebugger.PopMessage(msg))
|
|
{
|
|
break;
|
|
}
|
|
hadMessages = true;
|
|
int paramIdx = msg.IndexOf(' ');
|
|
String cmd = scope String();
|
|
String param = scope String();
|
|
|
|
if (paramIdx > 0)
|
|
{
|
|
cmd.Append(msg, 0, paramIdx);
|
|
param.Append(msg, paramIdx + 1);
|
|
}
|
|
else
|
|
cmd.Append(msg);
|
|
|
|
bool isOutput = (cmd == "msg") || (cmd == "dbgEvalMsg") || (cmd == "log");
|
|
if (cmd == "msgLo")
|
|
{
|
|
if (mVerbosity < .Diagnostic)
|
|
continue;
|
|
isOutput = true;
|
|
}
|
|
|
|
if (isOutput)
|
|
{
|
|
if (deferredMsgType != cmd)
|
|
{
|
|
if (deferredOutput.Length > 0)
|
|
{
|
|
OutputFormatted(deferredOutput, deferredMsgType == "dbgEvalMsg");
|
|
deferredOutput.Clear();
|
|
}
|
|
|
|
deferredMsgType.Set(cmd);
|
|
}
|
|
|
|
|
|
deferredOutput.Append(param);
|
|
}
|
|
else if (cmd == "error")
|
|
{
|
|
if ((mRunningTestScript) && (!IsCrashDump) && (!mScriptManager.IsErrorExpected(param)))
|
|
mScriptManager.Fail(param);
|
|
|
|
bool isFirstMsg = true;
|
|
|
|
String tempStr = scope String();
|
|
bool focusOutput = false;
|
|
|
|
while (true)
|
|
{
|
|
String errorMsg = null;
|
|
|
|
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.Remove(0, endPos + 1);
|
|
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.Remove(0, infoPos);
|
|
}
|
|
else
|
|
errorMsg = param;
|
|
|
|
if (errorMsg != null)
|
|
{
|
|
if (isFirstMsg)
|
|
{
|
|
OutputLineSmart(scope String("ERROR: ", errorMsg));
|
|
if (gApp.mRunningTestScript)
|
|
{
|
|
// The 'OutputLineSmart' would already call 'Fail' when running test scripts
|
|
}
|
|
else
|
|
{
|
|
Fail(errorMsg);
|
|
}
|
|
isFirstMsg = false;
|
|
}
|
|
else
|
|
Output(errorMsg);
|
|
}
|
|
|
|
if (infoPos == -1)
|
|
break;
|
|
}
|
|
|
|
if (focusOutput)
|
|
ShowOutput();
|
|
}
|
|
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;
|
|
}
|
|
String infoString = StackStringFormat!("Memory breakpoint hit: '0x{0:X08}'", (int64)memoryAddress);
|
|
if (breakpoint != null)
|
|
infoString = StackStringFormat!("Memory breakpoint hit: '0x{0:X08}' ({1})", (int64)memoryAddress, breakpoint.mMemoryWatchExpression);
|
|
OutputLine(infoString);
|
|
if (!mRunningTestScript)
|
|
{
|
|
MessageDialog("Breakpoint Hit", infoString);
|
|
Beep(MessageBeepType.Information);
|
|
}
|
|
}
|
|
else if (cmd == "newProfiler")
|
|
{
|
|
var profiler = mDebugger.PopProfiler();
|
|
//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);
|
|
}
|
|
else
|
|
{
|
|
Runtime.FatalError("Invalid debugger message type");
|
|
}
|
|
}
|
|
|
|
if (deferredOutput.Length > 0)
|
|
{
|
|
OutputFormatted(deferredOutput, deferredMsgType == "dbgEvalMsg");
|
|
}
|
|
|
|
/*if (hadMessages)
|
|
mNoDebugMessagesTick = 0;
|
|
else if (IDEApp.sApp.mIsUpdateBatchStart)
|
|
mNoDebugMessagesTick++;
|
|
if (mNoDebugMessagesTick < 10)
|
|
wantDebuggerDetach = false; // Don't detach if we have messages*/
|
|
|
|
if (wantDebuggerDetach)
|
|
{
|
|
OutputLine("Detached debugger");
|
|
|
|
UpdateTitle();
|
|
var disassemblyPanel = TryGetDisassemblyPanel(false);
|
|
if (disassemblyPanel != null)
|
|
disassemblyPanel.Disable();
|
|
mDebugger.DisposeNativeBreakpoints();
|
|
mDebugger.Detach();
|
|
mDebugger.mIsRunning = false;
|
|
mExecutionPaused = false;
|
|
mHotResolveState = .None;
|
|
mDebuggerPerformingTask = false;
|
|
mWorkspace.StoppedRunning();
|
|
mBreakpointPanel.BreakpointsChanged();
|
|
mModulePanel.ModulesChanged();
|
|
if (mPendingDebugExprHandler != null)
|
|
{
|
|
mPendingDebugExprHandler(null);
|
|
DeleteAndNullify!(mPendingDebugExprHandler);
|
|
}
|
|
mMemoryPanel.SetDisabled(true);
|
|
WithWatchPanels(scope (watchPanel) =>
|
|
{
|
|
watchPanel.SetDisabled(true);
|
|
});
|
|
mIsAttachPendingSourceShow = false;
|
|
}
|
|
|
|
if ((mDebugger != null) && (mDebugger.IsPaused()) && (!mDebugger.HasPendingDebugLoads()))
|
|
{
|
|
mForegroundTargetCountdown = 0;
|
|
if (!mExecutionPaused)
|
|
{
|
|
if (!mTargetDidInitBreak)
|
|
{
|
|
mTargetDidInitBreak = true;
|
|
bool wantsContinue = true;
|
|
if (mTargetStartWithStep)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
wantsContinue = true;
|
|
}
|
|
else
|
|
{
|
|
if (mMainBreakpoint != null)
|
|
{
|
|
if (mMainBreakpoint.IsActiveBreakpoint())
|
|
wantsContinue = true;
|
|
BfLog.LogDbg("Deleting mMainBreakpoint 2\n");
|
|
mDebugger.DeleteBreakpoint(mMainBreakpoint);
|
|
//mMainBreakpoint.Dispose();
|
|
mMainBreakpoint = null;
|
|
}
|
|
}
|
|
|
|
if (wantsContinue)
|
|
{
|
|
DebuggerUnpaused();
|
|
mDebugger.Continue();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (mMainBreakpoint != null)
|
|
{
|
|
BfLog.LogDbg("Deleting mMainBreakpoint 3\n");
|
|
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"))
|
|
{
|
|
// Okay, NOW we can do a "step into"
|
|
if (!IsInDisassemblyMode())
|
|
mDebugger.StepInto(false);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ((!HasModalDialogs()) && (!mRunningTestScript))
|
|
mMainWindow.SetForeground();
|
|
|
|
if (mRunTimingProfileId != 0)
|
|
{
|
|
mRunTimingProfileId.Dispose();
|
|
mRunTimingProfileId = 0;
|
|
}
|
|
|
|
BeefPerf.Event("Debugger Paused", "");
|
|
DebuggerPaused();
|
|
PCChanged();
|
|
mBreakpointPanel.MarkStatsDirty();
|
|
mTargetHadFirstBreak = true;
|
|
|
|
if (mDebugger.GetRunState() == DebugManager.RunState.Exception)
|
|
{
|
|
String exceptionLine = scope String();
|
|
mDebugger.GetCurrentException(exceptionLine);
|
|
var exceptionData = String.StackSplit!(exceptionLine, '\n');
|
|
|
|
String exHeader = StackStringFormat!("Exception {0}", exceptionData[1]);
|
|
if (exceptionData.Count >= 3)
|
|
exHeader = exceptionData[2];
|
|
|
|
String exString = StackStringFormat!("{0} at {1}", exHeader, exceptionData[0]);
|
|
|
|
OutputLine(exString);
|
|
if (!IsCrashDump)
|
|
Fail(exString);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
else if (mDebuggerPerformingTask)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (mForegroundTargetCountdown > 0)
|
|
{
|
|
if ((--mForegroundTargetCountdown == 0) && (mDebugger.mIsRunning))
|
|
mDebugger.ForegroundTarget();
|
|
}
|
|
|
|
if ((mDebugger != null) && (mExecutionPaused) && (mDebugger.mIsRunning))
|
|
DebuggerUnpaused();
|
|
break;
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
mErrorsPanel?.UpdateAlways();
|
|
}
|
|
|
|
public void ShowPassOutput(BfPassInstance bfPassInstance)
|
|
{
|
|
while (true)
|
|
{
|
|
var outString = scope String();
|
|
if (!bfPassInstance.PopOutString(outString))
|
|
break;
|
|
OutputLine(outString);
|
|
}
|
|
}
|
|
|
|
/*public bool CheckMouseover(Widget checkWidget, int32 wantTicks, out Point mousePoint)
|
|
{
|
|
mousePoint = Point(Int32.MinValue, Int32.MinValue);
|
|
if (checkWidget != mLastMouseWidget)
|
|
return false;
|
|
checkWidget.RootToSelfTranslate(mLastRelMousePos.x, mLastRelMousePos.y, out mousePoint.x, out mousePoint.y);
|
|
return mMouseStillTicks == wantTicks;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
void UpdateMouseover()
|
|
{
|
|
if (mMouseStillTicks != -1)
|
|
mMouseStillTicks++;
|
|
|
|
Widget overWidget = null;
|
|
int32 numOverWidgets = 0;
|
|
foreach (var window in mWindows)
|
|
{
|
|
var widgetWindow = window as WidgetWindow;
|
|
|
|
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)
|
|
{
|
|
SetLastMouseWidget(overWidget);
|
|
mMouseStillTicks = -1;
|
|
}
|
|
|
|
float actualX = widgetWindow.mClientX + widgetWindow.mMouseX;
|
|
float actualY = widgetWindow.mClientY + widgetWindow.mMouseY;
|
|
if ((mLastAbsMousePos.x != actualX) || (mLastAbsMousePos.y != actualY))
|
|
{
|
|
mMouseStillTicks = 0;
|
|
mLastAbsMousePos.x = actualX;
|
|
mLastAbsMousePos.y = actualY;
|
|
}
|
|
mLastRelMousePos.x = widgetWindow.mMouseX;
|
|
mLastRelMousePos.y = widgetWindow.mMouseY;
|
|
}
|
|
}
|
|
|
|
if (overWidget == null)
|
|
{
|
|
SetLastMouseWidget(null);
|
|
mMouseStillTicks = -1;
|
|
}
|
|
|
|
if (numOverWidgets > 1)
|
|
{
|
|
//int a = 0;
|
|
}
|
|
|
|
Debug.Assert(numOverWidgets <= 1);
|
|
}*/
|
|
|
|
public void FileRenamed(ProjectFileItem projectFileItem, String oldPath, String newPath)
|
|
{
|
|
String newFileName = scope String();
|
|
Path.GetFileName(newPath, newFileName);
|
|
|
|
RenameEditData(oldPath, newPath);
|
|
|
|
WithTabs(scope (tab) =>
|
|
{
|
|
var sourceViewPanel = tab.mContent as SourceViewPanel;
|
|
if (sourceViewPanel != null)
|
|
{
|
|
if (Path.Equals(sourceViewPanel.mFilePath, oldPath))
|
|
{
|
|
var sourceViewTab = (IDEApp.SourceViewTab)tab;
|
|
|
|
//TODO: We might have to resize the label here?
|
|
//sourceViewPanel.mFilePath.Set(newPath);
|
|
sourceViewPanel.PathChanged(newPath);
|
|
tab.Label = newFileName;
|
|
tab.mWantWidth = sourceViewTab.GetWantWidth();
|
|
//tab.mTabbedView.Resize();
|
|
}
|
|
}
|
|
});
|
|
|
|
var recentFileList = mRecentlyDisplayedFiles;
|
|
for (int32 recentFileIdx = 0; recentFileIdx < recentFileList.Count; recentFileIdx++)
|
|
{
|
|
if (recentFileList[recentFileIdx] == oldPath)
|
|
recentFileList[recentFileIdx].Set(newPath);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
|
|
ProjectListViewItem listViewItem = null;
|
|
mProjectPanel?.mProjectToListViewMap.TryGetValue(projectItem, out listViewItem);
|
|
|
|
String newName = null;
|
|
if (newPath != null)
|
|
{
|
|
newName = scope ::String();
|
|
Path.GetFileName(newPath, newName);
|
|
}
|
|
|
|
if (changeType == .Renamed)
|
|
{
|
|
if (projectItem.mIncludeKind == .Auto)
|
|
{
|
|
let projectFileItem = projectItem as ProjectFileItem;
|
|
|
|
if (listViewItem != null)
|
|
listViewItem.Label = newName;
|
|
|
|
String oldPath = scope String();
|
|
projectFileItem.GetFullImportPath(oldPath);
|
|
projectFileItem.Rename(newName);
|
|
|
|
FileRenamed(projectFileItem, oldPath, newPath);
|
|
mProjectPanel?.SortItem((ProjectListViewItem)listViewItem.mParentItem);
|
|
}
|
|
}
|
|
else if (changeType == .Deleted)
|
|
{
|
|
if (projectItem.mIncludeKind == .Auto)
|
|
{
|
|
mProjectPanel?.DoDeleteItem(listViewItem, null, .ForceRemove);
|
|
}
|
|
}
|
|
else if (changeType == .FileCreated)
|
|
{
|
|
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();
|
|
|
|
if (mProjectPanel != null)
|
|
{
|
|
mProjectPanel.AddProjectItem(projectSource);
|
|
mProjectPanel.QueueSortItem(listViewItem);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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();
|
|
|
|
if (mProjectPanel != null)
|
|
{
|
|
mProjectPanel.AddProjectItem(newFolder);
|
|
mProjectPanel.QueueSortItem(listViewItem);
|
|
}
|
|
|
|
newFolder.mAutoInclude = true;
|
|
if (mProjectPanel != null)
|
|
mProjectPanel.QueueRehupFolder(newFolder);
|
|
}
|
|
}
|
|
}
|
|
else if (changeType == .Failed)
|
|
{
|
|
if (mProjectPanel != null)
|
|
{
|
|
if (let projectFolder = projectItem as ProjectFolder)
|
|
mProjectPanel.QueueRehupFolder(projectFolder);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UpdateWorkspace()
|
|
{
|
|
mFileWatcher.Update();
|
|
|
|
bool appHasFocus = false;
|
|
for (var window in mWindows)
|
|
appHasFocus |= window.mHasFocus;
|
|
|
|
if (mRunningTestScript)
|
|
appHasFocus = true;
|
|
|
|
// Is this enough to get the behavior we want?
|
|
if (!appHasFocus)
|
|
return;
|
|
|
|
if (mLastFileChangeId != mFileWatcher.mChangeId)
|
|
{
|
|
if ((mFileChangedDialog != null) && (!mFileChangedDialog.mClosed))
|
|
{
|
|
mFileChangedDialog.Rehup();
|
|
}
|
|
mLastFileChangeId = mFileWatcher.mChangeId;
|
|
}
|
|
|
|
var app = gApp;
|
|
Debug.Assert(app != null);
|
|
|
|
while (true)
|
|
{
|
|
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 ((mFileChangedDialog != null) && (mFileChangedDialog.mDialogKind == .Deleted))
|
|
{
|
|
for (let fileName in mFileChangedDialog.mFileNames)
|
|
{
|
|
mFileWatcher.RemoveChangedFile(fileName);
|
|
}
|
|
}
|
|
|
|
while (/*(appHasFocus) && */((mFileChangedDialog == null) || (mFileChangedDialog.mClosed)))
|
|
{
|
|
while (mExternalChangeDeferredOpen.Count > 0)
|
|
{
|
|
String filePath = mExternalChangeDeferredOpen.PopFront();
|
|
ShowSourceFile(filePath);
|
|
delete filePath;
|
|
}
|
|
|
|
var changedFile = mFileWatcher.PopChangedFile();
|
|
if (changedFile == null)
|
|
{
|
|
if (mFileChangedDialog != null)
|
|
mFileChangedDialog = null;
|
|
break;
|
|
}
|
|
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;
|
|
editData.SetSavedData(null, IdSpan());
|
|
|
|
if (editData.mQueuedContent == null)
|
|
editData.mQueuedContent = new String();
|
|
editData.mQueuedContent.Clear();
|
|
if (LoadTextFile(fileName, editData.mQueuedContent, false, scope() =>
|
|
{
|
|
editData.BuildHash(editData.mQueuedContent);
|
|
}) case .Err(let err))
|
|
{
|
|
if (err case .FileOpenError(.SharingViolation))
|
|
{
|
|
// Put back on the list and try later
|
|
mFileWatcher.AddChangedFile(changedFile);
|
|
changedFile = null;
|
|
break;
|
|
}
|
|
editData.mFileDeleted = true;
|
|
}
|
|
|
|
using (mMonitor.Enter())
|
|
{
|
|
for (var projectSource in editData.mProjectSources)
|
|
{
|
|
projectSource.HasChangedSinceLastCompile = true;
|
|
}
|
|
}
|
|
|
|
var sourceViewPanel = FindSourceViewPanel(fileName);
|
|
if (sourceViewPanel != null)
|
|
{
|
|
FileChangedDialog.DialogKind dialogKind = File.Exists(fileName) ? .Changed : .Deleted;
|
|
|
|
if ((sourceViewPanel.mEditData != null) && (sourceViewPanel.mEditData.mFileDeleted))
|
|
sourceViewPanel.mEditData.IsFileDeleted(); // Rehup
|
|
|
|
if ((sourceViewPanel.HasUnsavedChanges()) || (dialogKind == .Deleted))
|
|
{
|
|
if ((mFileChangedDialog != null) && (mFileChangedDialog.mApplyAllResult[(int)dialogKind].HasValue))
|
|
{
|
|
bool closeAll = mFileChangedDialog.mApplyAllResult[(int)dialogKind].Value;
|
|
if (closeAll)
|
|
gApp.CloseDocument(sourceViewPanel);
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
mFileChangedDialog = new FileChangedDialog();
|
|
mFileChangedDialog.Show(sourceViewPanel);
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
sourceViewPanel.Reload();
|
|
//FileChanged(fileName);
|
|
}
|
|
}
|
|
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);
|
|
mBfResolveCompiler.QueueDeferredResolveAll();
|
|
mBfResolveCompiler.QueueRefreshViewCommand();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if !CLI
|
|
void UpdateIPC()
|
|
{
|
|
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)
|
|
{
|
|
mIPCHelper = new IPCHelper();
|
|
if (!mIPCHelper.Init("BF_IDE"))
|
|
DeleteAndNullify!(mIPCHelper);
|
|
else
|
|
{
|
|
#if !DEBUG
|
|
OutputLine("IPC: Hosting");//
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#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))
|
|
{
|
|
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) =>
|
|
{
|
|
DoOpenWorkspace(true);
|
|
});
|
|
dialog.AddButton("No", new (evt) =>
|
|
{
|
|
DeleteAndNullify!(mDeferredOpenFileName);
|
|
});
|
|
dialog.PopupWindow(GetActiveWindow());
|
|
return;
|
|
}
|
|
else if (!IDEUtils.IsDirectoryEmpty(directoryPath))
|
|
{
|
|
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) =>
|
|
{
|
|
DoOpenWorkspace(true);
|
|
});
|
|
dialog.AddButton("No", new (evt) =>
|
|
{
|
|
DeleteAndNullify!(mDeferredOpenFileName);
|
|
});
|
|
dialog.PopupWindow(GetActiveWindow());
|
|
return;
|
|
}
|
|
}
|
|
|
|
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();
|
|
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:
|
|
}
|
|
}
|
|
|
|
public override void Update(bool batchStart)
|
|
{
|
|
scope AutoBeefPerf("IDEApp.Update");
|
|
|
|
/*using (mWorkspace.mMonitor.Enter())
|
|
{
|
|
|
|
}*/
|
|
|
|
if (mWantShowOutput)
|
|
{
|
|
ShowOutput();
|
|
mWantShowOutput = false;
|
|
}
|
|
|
|
if (mDbgFastUpdate)
|
|
{
|
|
RefreshRate = 240;
|
|
MarkDirty();
|
|
}
|
|
else
|
|
{
|
|
RefreshRate = 60;
|
|
}
|
|
|
|
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
|
|
if (mSettings.mEnableDevMode)
|
|
UpdateIPC();
|
|
#endif
|
|
|
|
if (mRunTest)
|
|
{
|
|
if (mRunTestDelay > 0)
|
|
{
|
|
mRunTestDelay--;
|
|
}
|
|
else if (IsCompiling)
|
|
{
|
|
|
|
}
|
|
else if (mDebugger.mIsRunning)
|
|
{
|
|
mDebugger.Terminate();
|
|
mRunTestDelay = 30;
|
|
}
|
|
else
|
|
{
|
|
mTargetStartWithStep = false;
|
|
CompileAndRun(true);
|
|
mRunTestDelay = 200;
|
|
}
|
|
}
|
|
|
|
if (mProfilePanel != null)
|
|
mProfilePanel.ForcedUpdate();
|
|
if (mLongUpdateProfileId != 0)
|
|
DoLongUpdateCheck();
|
|
|
|
if (mWakaTime != null)
|
|
mWakaTime.Update();
|
|
if (mFindResultsPanel != null)
|
|
mFindResultsPanel.Update();
|
|
if (mBfResolveSystem != null)
|
|
mBfResolveSystem.PerfZoneStart("IDEApp.Update");
|
|
UpdateWorkspace();
|
|
if (ThemeFactory.mDefault != null)
|
|
ThemeFactory.mDefault.Update();
|
|
base.Update(batchStart);
|
|
|
|
DarkTooltipManager.UpdateTooltip();
|
|
mGlobalUndoManager.Update();
|
|
|
|
for (var project in mWorkspace.mProjects)
|
|
project.Update();
|
|
UpdateCompilersAndDebugger();
|
|
if (mScriptManager != null)
|
|
mScriptManager.Update();
|
|
|
|
if (mTestManager != null)
|
|
{
|
|
mTestManager.Update();
|
|
if (mTestManager.mIsDone)
|
|
{
|
|
if (mMainFrame != null)
|
|
mMainFrame.mStatusBar.SelectConfig(mTestManager.mPrevConfigName);
|
|
DeleteAndNullify!(mTestManager);
|
|
}
|
|
}
|
|
|
|
UpdateExecution();
|
|
if (mBfResolveSystem != null)
|
|
mBfResolveSystem.PerfZoneEnd();
|
|
|
|
UpdateDeferredOpens();
|
|
|
|
mHaveSourcesChangedInternallySinceLastCompile = false;
|
|
WithSourceViewPanels(scope (sourceViewPanel) =>
|
|
{
|
|
mHaveSourcesChangedInternallySinceLastCompile |= sourceViewPanel.mHasChangedSinceLastCompile;
|
|
});
|
|
|
|
if ((mCloseDelay > 0) && (--mCloseDelay == 0))
|
|
mMainWindow.Close();
|
|
|
|
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 (IDEApp.sApp.mSpellChecker != null)
|
|
IDEApp.sApp.mSpellChecker.CheckThreadDone();
|
|
|
|
///
|
|
//TODO: REMOVE
|
|
//mDebugger.InitiateHotResolve(.ActiveMethods | .Allocations);
|
|
}
|
|
|
|
public override void Draw()
|
|
{
|
|
scope AutoBeefPerf("IDEApp.Draw");
|
|
#if CLI
|
|
return;
|
|
#endif
|
|
#unwarn
|
|
if (mBfResolveSystem != null)
|
|
{
|
|
mBfResolveSystem.PerfZoneStart("IDEApp.Draw");
|
|
base.Draw();
|
|
mBfResolveSystem.PerfZoneEnd();
|
|
if (mBfResolveSystem.mIsTiming)
|
|
{
|
|
mBfResolveSystem.StopTiming();
|
|
mBfResolveSystem.DbgPrintTimings();
|
|
}
|
|
}
|
|
else
|
|
base.Draw();
|
|
}
|
|
|
|
public void DrawSquiggle(Graphics g, float x, float y, float width)
|
|
{
|
|
Image squiggleImage = IDEApp.sApp.mSquiggleImage;
|
|
int32 segSize = 30;
|
|
float height = mSquiggleImage.mHeight;
|
|
|
|
float curX = x;
|
|
float curWidth = width;
|
|
|
|
float drawY = y + gApp.mCodeFont.GetHeight();
|
|
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)
|
|
{
|
|
#if BF_PLATFORM_WINDOWS
|
|
MessageBeep(type);
|
|
#endif
|
|
}
|
|
|
|
#if BF_PLATFORM_WINDOWS
|
|
[Import("user32.lib"), CLink, CallingConvention(.Stdcall)]
|
|
public static extern bool MessageBeep(MessageBeepType type);
|
|
#endif
|
|
}
|
|
|
|
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
|
|
}*/
|
|
}
|
|
}
|