2019-08-23 11:56:54 -07:00
using System ;
using System.Collections.Generic ;
using System.Reflection ;
using System.IO ;
using System.Diagnostics ;
using IDE.ui ;
using Beefy ;
using Beefy.widgets ;
using IDE.Debugger ;
using Beefy.theme.dark ;
using System.Threading ;
using System.Security.Cryptography ;
namespace IDE
{
class ScriptManager
{
2019-08-27 08:04:41 -07:00
public static ScriptManager sActiveManager ;
2019-08-23 11:56:54 -07:00
class Target
{
public class Cmd
{
public MethodInfo mMethodInfo ;
public Object mTargetObject ;
}
public Dictionary < String , Target > mTargets = new . ( ) ~ DeleteDictionyAndKeysAndItems ! ( _ ) ;
public Dictionary < String , Cmd > mCmds = new . ( ) ~ DeleteDictionyAndKeysAndItems ! ( _ ) ;
}
2019-08-27 08:04:41 -07:00
public enum CmdFlags
{
None ,
NoLines
}
2019-08-23 11:56:54 -07:00
public class QueuedCmd
{
public String mCondition ~ delete _ ;
2019-08-27 08:04:41 -07:00
public CmdFlags mFlags ;
2019-08-23 11:56:54 -07:00
public bool mHandled = true ;
public String mCmd ~ delete _ ;
public String mSrcFile ~ delete _ ;
public int mLineNum = - 1 ;
public int mIntParam ;
2019-08-27 08:04:41 -07:00
public int mExecIdx ;
2019-08-23 11:56:54 -07:00
public bool mNoWait ;
2019-08-27 08:04:41 -07:00
public Stopwatch mStopWatch ~ delete _ ;
2019-08-23 11:56:54 -07:00
}
2019-08-27 08:04:41 -07:00
ScriptHelper mScriptHelper = new ScriptHelper ( this ) ~ delete _ ;
2019-08-23 11:56:54 -07:00
Target mRoot = new Target ( ) ~ delete _ ;
Dictionary < String , Variant > mVars = new . ( ) ~
{
for ( var kv in _ )
{
delete kv . key ;
kv . value . Dispose ( ) ;
}
delete _ ;
} ;
List < QueuedCmd > mCmdList = new . ( ) ~ DeleteContainerAndItems ! ( _ ) ;
2019-08-27 08:04:41 -07:00
public bool mFailed ;
public bool mCancelled ;
2019-08-23 11:56:54 -07:00
public QueuedCmd mCurCmd ;
public Stopwatch mTimeoutStopwatch ~ delete _ ;
public int mTimeoutMS ;
public String mExpectingError ~ delete _ ;
public bool mHadExpectingError ;
public int mDoneTicks ;
2019-08-27 08:04:41 -07:00
public bool mAllowCompiling ;
public bool mSoftFail ;
public Verbosity mVerbosity = . Quiet ;
public String mProjectName ~ delete _ ;
2019-08-23 11:56:54 -07:00
public bool Failed
{
get
{
return mFailed ;
}
}
public bool HasQueuedCommands
{
get
{
return ! mCmdList . IsEmpty ;
}
}
public this ( )
{
AddTarget ( mScriptHelper ) ;
//Exec("OutputLine(\"Hey bro!\", 2)");
}
public void MarkNotHandled ( )
{
if ( mCurCmd ! = null )
mCurCmd . mHandled = false ;
}
public void AddTarget ( Object targetObject )
{
var targetType = targetObject . GetType ( ) ;
for ( var methodInfo in targetType . GetMethods ( . Instance | . Public | . NonPublic ) )
{
var methodName = methodInfo . Name ;
if ( methodName . StartsWith ( "Cmd_" ) )
methodName = . ( methodName , 4 ) ;
Target curTarget = mRoot ;
while ( true )
{
int splitPos = methodName . IndexOf ( "__" ) ;
if ( splitPos ! = - 1 )
{
StringView cmdPart = . ( methodName , 0 , splitPos ) ;
String * keyPtr ;
Target * targetPtr ;
if ( curTarget . mTargets . TryAdd ( scope String ( cmdPart ) , out keyPtr , out targetPtr ) )
{
* keyPtr = new String ( cmdPart ) ;
* targetPtr = new Target ( ) ;
}
curTarget = * targetPtr ;
methodName . RemoveFromStart ( splitPos + 2 ) ;
}
else
{
String * keyPtr ;
Target . Cmd * cmdPtr ;
if ( curTarget . mCmds . TryAdd ( scope String ( methodName ) , out keyPtr , out cmdPtr ) )
{
* keyPtr = new String ( methodName ) ;
* cmdPtr = new . ( ) ;
let cmd = * cmdPtr ;
cmd . mMethodInfo = methodInfo ;
cmd . mTargetObject = targetObject ;
}
break ;
}
}
}
}
public void Fail ( StringView err )
{
if ( mFailed )
return ;
mFailed = true ;
var errStr = scope String ( err ) ;
if ( mCurCmd ! = null )
{
2019-08-27 08:04:41 -07:00
if ( mCurCmd . mFlags . HasFlag ( . NoLines ) )
errStr . AppendF ( " in {}\n\t{}" , mCurCmd . mSrcFile , mCurCmd . mCmd ) ;
else
errStr . AppendF ( " at line {} in {}\n\t{}" , mCurCmd . mLineNum + 1 , mCurCmd . mSrcFile , mCurCmd . mCmd ) ;
2019-08-23 11:56:54 -07:00
}
2019-08-27 08:04:41 -07:00
if ( mSoftFail )
gApp . OutputErrorLine ( errStr ) ;
else
gApp . Fail ( errStr ) ;
2019-08-23 11:56:54 -07:00
//TODO:
//gApp.mRunningTestScript = false;
}
2019-09-03 11:17:13 -07:00
public bool IsErrorExpected ( StringView err )
{
return ( mExpectingError ! = null ) & & ( err . Contains ( mExpectingError ) ) ;
}
2019-08-23 11:56:54 -07:00
public void Fail ( StringView fmt , params Object [ ] args )
{
Fail ( scope String ( ) . . AppendF ( fmt , params args ) ) ;
}
public void Clear ( )
{
DeleteAndClearItems ! ( mCmdList ) ;
mFailed = false ;
2019-08-29 14:19:07 -07:00
mCurCmd = null ;
2019-08-23 11:56:54 -07:00
}
2019-08-27 08:04:41 -07:00
public void QueueCommands ( StreamReader streamReader , StringView filePath , CmdFlags flags )
2019-08-23 11:56:54 -07:00
{
int lineNum = 0 ;
for ( var lineResult in streamReader . Lines )
Line :
{
switch ( lineResult )
{
case . Ok ( var line ) :
line . Trim ( ) ;
if ( ( ! line . IsEmpty ) & & ( ! line . StartsWith ( "#" ) ) )
{
QueuedCmd queuedCmd = new . ( ) ;
2019-08-27 08:04:41 -07:00
queuedCmd . mFlags = flags ;
2019-08-23 11:56:54 -07:00
queuedCmd . mSrcFile = new String ( filePath ) ;
queuedCmd . mLineNum = lineNum ;
if ( line . StartsWith ( "nowait " ) )
{
queuedCmd . mNoWait = true ;
line . RemoveFromStart ( "no wait" . Length ) ;
}
if ( line . StartsWith ( "if " ) )
{
int parenCount = 0 ;
int strPos = 2 ;
while ( strPos < line . Length )
{
char8 c = line [ strPos ] ;
if ( c = = '(' )
{
parenCount + + ;
}
else if ( c = = ')' )
{
parenCount - - ;
if ( parenCount = = 0 )
break ;
}
strPos + + ;
}
queuedCmd . mCondition = new String ( line , 4 , strPos - 4 ) ;
line . RemoveFromStart ( strPos + 1 ) ;
line . Trim ( ) ;
}
queuedCmd . mCmd = new String ( line ) ;
mCmdList . Add ( queuedCmd ) ;
}
lineNum + + ;
case . Err :
Fail ( "Failed reading from file '{0}'" , filePath ) ;
}
}
}
2019-08-27 08:04:41 -07:00
public void QueueCommands ( StringView cmds , StringView filePath , CmdFlags flags )
{
StringStream strStream = scope . ( cmds , . Reference ) ;
StreamReader reader = scope . ( strStream ) ;
QueueCommands ( reader , filePath , flags ) ;
}
2019-08-23 11:56:54 -07:00
public void QueueCommandFile ( StringView filePath )
{
let streamReader = scope StreamReader ( ) ;
if ( streamReader . Open ( filePath ) case . Err )
{
Fail ( "Unable to open command file '{0}'" , filePath ) ;
return ;
}
2019-08-27 08:04:41 -07:00
QueueCommands ( streamReader , filePath , . None ) ;
2019-08-23 11:56:54 -07:00
}
public void SetTimeoutMS ( int timeoutMS )
{
if ( mTimeoutStopwatch = = null )
{
mTimeoutStopwatch = new . ( ) ;
mTimeoutStopwatch . Start ( ) ;
}
mTimeoutMS = timeoutMS ;
}
2019-08-27 08:04:41 -07:00
public void Exec ( StringView cmd )
2019-08-23 11:56:54 -07:00
{
2019-08-27 08:04:41 -07:00
var cmd ;
cmd . Trim ( ) ;
if ( ( cmd . StartsWith ( "#" ) ) | | ( cmd . IsEmpty ) )
return ;
if ( cmd . StartsWith ( "%exec " ) )
{
mScriptHelper . ExecuteRaw ( scope String ( cmd , "%exec " . Length ) ) ;
return ;
}
if ( cmd . StartsWith ( "%targetComplete " ) )
{
let projectName = cmd . Substring ( "%targetComplete " . Length ) ;
if ( gApp . mExecutionQueue . IsEmpty )
return ;
2019-08-23 11:56:54 -07:00
2019-08-27 08:04:41 -07:00
bool matched = false ;
if ( var targetCompleteCmd = gApp . mExecutionQueue [ 0 ] as IDEApp . TargetCompletedCmd )
{
if ( targetCompleteCmd . mProject . mProjectName = = projectName )
{
targetCompleteCmd . mIsReady = true ;
matched = true ;
}
}
if ( ! matched )
{
mCurCmd . mHandled = false ;
}
2019-08-23 11:56:54 -07:00
return ;
2019-08-27 08:04:41 -07:00
}
if ( mCurCmd . mExecIdx = = 0 )
{
if ( mVerbosity > = . Normal )
{
gApp . OutputLine ( "Executing Command: {}" , cmd ) ;
if ( mVerbosity > = . Detailed )
{
mCurCmd . mStopWatch = new . ( true ) ;
}
}
}
2019-08-23 11:56:54 -07:00
StringView varName = . ( ) ;
/ * int eqPos = cmdLineView . IndexOf ( '=' ) ;
if ( eqPos ! = - 1 )
{
varName = StringView ( cmdLineView , eqPos ) ;
varName . Trim ( ) ;
cmdLineView . RemoveFromStart ( eqPos + 1 ) ;
cmdLineView . Clear ( ) ;
} * /
StringView methodName ;
List < Object > args = scope . ( ) ;
2019-08-27 08:04:41 -07:00
int parenPos = cmd . IndexOf ( '(' ) ;
2019-08-23 11:56:54 -07:00
if ( parenPos ! = - 1 )
{
2019-08-27 08:04:41 -07:00
methodName = cmd . Substring ( 0 , parenPos ) ;
2019-08-23 11:56:54 -07:00
methodName . Trim ( ) ;
2019-08-27 08:04:41 -07:00
int endParenPos = cmd . LastIndexOf ( ')' ) ;
2019-08-23 11:56:54 -07:00
if ( endParenPos = = - 1 )
{
Fail ( "Missing argument end ')'" ) ;
return ;
}
2019-08-27 08:04:41 -07:00
var postStr = StringView ( cmd , endParenPos + 1 ) ;
2019-08-23 11:56:54 -07:00
postStr . Trim ( ) ;
if ( ( ! postStr . IsEmpty ) & & ( ! postStr . StartsWith ( "#" ) ) )
{
Fail ( "Invalid string following command" ) ;
return ;
}
2019-08-27 08:04:41 -07:00
Workspace . Options workspaceOptions = null ;
Project project = null ;
Project . Options projectOptions = null ;
2019-08-23 11:56:54 -07:00
bool inQuotes = false ;
int startIdx = parenPos ;
for ( int idx = parenPos ; idx < = endParenPos ; idx + + )
{
2019-08-27 08:04:41 -07:00
char8 c = cmd [ idx ] ;
2019-08-23 11:56:54 -07:00
if ( c = = '\\' )
{
// Skip past slashed strings
idx + + ;
continue ;
}
else if ( c = = '"' )
{
inQuotes ^ = true ;
}
else if ( ( ( c = = ',' ) | | ( c = = ')' ) ) & & ( ! inQuotes ) )
{
2019-08-27 08:04:41 -07:00
StringView argView = cmd . Substring ( startIdx + 1 , idx - startIdx - 1 ) ;
2019-08-23 11:56:54 -07:00
argView . Trim ( ) ;
if ( argView . IsEmpty )
continue ;
if ( argView . StartsWith ( "\"" ) )
{
var str = scope : : String ( ) ;
if ( argView . UnQuoteString ( str ) case . Err )
Fail ( "Failed to unquote string" ) ;
if ( str . Contains ( '$' ) )
{
2019-08-27 08:04:41 -07:00
if ( workspaceOptions = = null )
{
workspaceOptions = gApp . GetCurWorkspaceOptions ( ) ;
if ( mProjectName ! = null )
{
project = gApp . mWorkspace . FindProject ( mProjectName ) ;
if ( project ! = null )
projectOptions = gApp . GetCurProjectOptions ( project ) ;
}
}
2019-08-23 11:56:54 -07:00
String newStr = scope : : . ( ) ;
String err = scope . ( ) ;
2019-08-27 08:04:41 -07:00
if ( ! gApp . DoResolveConfigString ( workspaceOptions , project , projectOptions , str , err , newStr ) )
2019-08-23 11:56:54 -07:00
{
Fail ( scope String ( ) . . AppendF ( "Unknown macro string '{}' in '{}'" , err , str ) ) ;
}
str = newStr ;
}
args . Add ( str ) ;
}
else if ( argView . EndsWith ( 'f' ) )
{
switch ( float . Parse ( argView ) )
{
case . Ok ( let val ) :
args . Add ( scope : : box val ) ;
case . Err :
Fail ( "Failed to parse float" ) ;
return ;
}
}
else if ( argView . Contains ( '.' ) )
{
switch ( double . Parse ( argView ) )
{
case . Ok ( let val ) :
args . Add ( scope : : box val ) ;
case . Err :
Fail ( "Failed to parse double" ) ;
return ;
}
}
else // Integer
{
switch ( int . Parse ( argView ) )
{
case . Ok ( let val ) :
args . Add ( scope : : box val ) ;
case . Err :
Fail ( "Failed to parse int" ) ;
return ;
}
}
startIdx = idx ;
}
}
/ * for ( var argView in cmdLineView . Substring ( parenPos + 1 , endParenPos - parenPos - 1 ) . Split ( ',' ) )
{
HandleArg ( argView ) ;
} * /
}
else
{
2019-08-27 08:04:41 -07:00
methodName = cmd ;
2019-08-23 11:56:54 -07:00
}
2019-08-29 14:19:07 -07:00
if ( mFailed )
return ;
2019-08-23 11:56:54 -07:00
Target curTarget = mRoot ;
for ( var cmdPart in methodName . Split ( '.' ) )
{
if ( @cmdPart . HasMore )
{
if ( ! curTarget . mTargets . TryGetValue ( scope String ( cmdPart ) , out curTarget ) )
{
Fail ( "Unable to find target '{0}'" , cmdPart ) ;
return ;
}
}
else
{
Target . Cmd cmd ;
if ( ! curTarget . mCmds . TryGetValue ( scope String ( cmdPart ) , out cmd ) )
{
Fail ( "Unable to find command '{0}'" , cmdPart ) ;
return ;
}
Object [ ] argsArr = scope Object [ args . Count ] ;
args . CopyTo ( argsArr ) ;
switch ( cmd . mMethodInfo . Invoke ( cmd . mTargetObject , params argsArr ) )
{
case . Err :
Fail ( "Failed to invoke command" ) ;
return ;
case . Ok ( var result ) :
if ( ! varName . IsEmpty )
{
String * keyPtr ;
Variant * valuePtr ;
if ( mVars . TryAdd ( scope String ( varName ) , out keyPtr , out valuePtr ) )
* keyPtr = new String ( varName ) ;
else
valuePtr . Dispose ( ) ;
* valuePtr = result ;
}
else
result . Dispose ( ) ;
}
}
}
}
public bool CheckCondition ( StringView condition )
{
StringView curStr = . ( ) ;
BumpAllocator tempAlloc = scope . ( ) ;
StringView NextToken ( )
{
if ( curStr . IsEmpty )
return . ( ) ;
char8 * idStart = null ;
while ( true )
{
char8 c = 0 ;
if ( ! curStr . IsEmpty )
{
c = curStr [ 0 ] ;
curStr . RemoveFromStart ( 1 ) ;
}
if ( c = = ' ' )
continue ;
char8 nextC = 0 ;
if ( ! curStr . IsEmpty )
nextC = curStr [ 0 ] ;
if ( idStart ! = null )
{
if ( * idStart = = '"' )
{
if ( c = = '"' )
return . ( idStart , curStr . Ptr - idStart ) ;
}
else if ( ! nextC . IsLetterOrDigit )
{
return . ( idStart , curStr . Ptr - idStart ) ;
}
}
else
{
if ( c = = 0 )
return . ( ) ;
if ( ( c = = '!' ) & & ( nextC = = '=' ) )
{
curStr . RemoveFromStart ( 1 ) ;
return StringView ( curStr . Ptr - 2 , 2 ) ;
}
if ( ( c = = '=' ) & & ( nextC = = '=' ) )
{
curStr . RemoveFromStart ( 1 ) ;
return StringView ( curStr . Ptr - 2 , 2 ) ;
}
idStart = curStr . Ptr - 1 ;
}
}
}
Object Evaluate ( StringView token )
{
if ( token . StartsWith ( "\"" ) )
return new : tempAlloc String ( token , 1 , token . Length - 2 ) ;
if ( token = = "platform" )
return gApp . mPlatformName ;
else if ( token = = "config" )
return gApp . mConfigName ;
2019-08-29 14:19:07 -07:00
else if ( token = = "optlevel" )
{
var workspaceOptions = gApp . GetCurWorkspaceOptions ( ) ;
if ( workspaceOptions ! = null )
{
String str = new : tempAlloc . ( ) ;
workspaceOptions . mBfOptimizationLevel . ToString ( str ) ;
return str ;
}
}
2019-08-23 11:56:54 -07:00
return null ;
}
bool Compare ( Object obj , Object obj2 )
{
if ( obj is String )
{
return ( String ) obj = = ( String ) obj2 ;
}
return obj = = obj2 ;
}
Object Evaluate ( )
{
StringView tok = NextToken ( ) ;
if ( tok . IsEmpty )
return null ;
Object val = Evaluate ( tok ) ;
if ( val = = null )
return val ;
while ( true )
{
StringView op = NextToken ( ) ;
if ( op . IsEmpty )
return val ;
StringView rhsToken = NextToken ( ) ;
Object rhs = Evaluate ( rhsToken ) ;
if ( rhs = = null )
return val ;
if ( op = = "!=" )
val = new : tempAlloc box ! Compare ( val , rhs ) ;
else if ( op = = "==" )
val = new : tempAlloc box Compare ( val , rhs ) ;
}
}
curStr = condition ;
Object result = Evaluate ( ) ;
if ( result is bool )
{
bool success = ( bool ) result ;
return success ;
}
else
{
Fail ( "Invalid result from expression" ) ;
}
return false ;
}
2019-08-27 08:04:41 -07:00
public void Cancel ( )
{
mCancelled = true ;
ClearAndDeleteItems ( mCmdList ) ;
}
2019-08-23 11:56:54 -07:00
public void Update ( )
{
if ( mFailed )
return ;
if ( ( mTimeoutMS > 0 ) & & ( gApp . mRunningTestScript ) )
{
if ( mTimeoutStopwatch . ElapsedMilliseconds > = mTimeoutMS )
2019-08-27 08:04:41 -07:00
Fail ( "Script has timed out: {:0.00}s" , mTimeoutStopwatch . ElapsedMilliseconds / 1000.0f ) ;
2019-08-23 11:56:54 -07:00
}
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager = this ;
2019-08-23 11:56:54 -07:00
while ( ( ! mCmdList . IsEmpty ) & & ( ! mFailed ) )
{
mCurCmd = mCmdList [ 0 ] ;
mCurCmd . mHandled = true ;
if ( ! mCurCmd . mNoWait )
{
if ( ! mScriptHelper . IsPaused ( ) )
break ;
// Only do a wait for the initial execution
// This is required for things like AssertEvalEquals that will be handled internally by repeated
// calls where 'mHandled = false' is set
mCurCmd . mNoWait = true ;
}
bool doExec = true ;
if ( mCurCmd . mCondition ! = null )
doExec = CheckCondition ( mCurCmd . mCondition ) ;
if ( doExec )
2019-08-27 08:04:41 -07:00
{
2019-08-23 11:56:54 -07:00
Exec ( mCurCmd . mCmd ) ;
2019-08-29 14:19:07 -07:00
mCurCmd ? . mExecIdx + + ;
2019-08-27 08:04:41 -07:00
}
2019-08-23 11:56:54 -07:00
if ( mCmdList . IsEmpty )
break ;
if ( ! mCurCmd . mHandled )
break ; // Try again next update
2019-08-27 08:04:41 -07:00
if ( mCurCmd . mStopWatch ! = null )
{
mCurCmd . mStopWatch . Stop ( ) ;
if ( mCurCmd . mStopWatch . ElapsedMilliseconds > 10 )
gApp . OutputLine ( "Command Time: {:0.00}s" , mCurCmd . mStopWatch . ElapsedMilliseconds / 1000.0f ) ;
}
2019-08-23 11:56:54 -07:00
mCmdList . RemoveAt ( 0 ) ;
delete mCurCmd ;
mCurCmd = null ;
}
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager = null ;
2019-08-23 11:56:54 -07:00
}
}
class ScriptHelper
{
public EditWidgetContent . LineAndColumn mMarkedPos ;
2019-08-27 08:04:41 -07:00
public ScriptManager mScriptManager ;
public this ( ScriptManager scriptManager )
{
mScriptManager = scriptManager ;
}
2019-08-23 11:56:54 -07:00
void FixFilePath ( String filePath , ProjectFolder folder )
{
for ( var entry in folder . mChildItems )
{
if ( var projectSource = entry as ProjectSource )
{
var fullPath = scope String ( ) ;
projectSource . GetFullImportPath ( fullPath ) ;
if ( fullPath . EndsWith ( filePath , . OrdinalIgnoreCase ) )
{
if ( filePath . Length ! = fullPath . Length )
{
let prevC = fullPath [ fullPath . Length - filePath . Length - 1 ] ;
if ( ( prevC ! = '\\' ) & & ( prevC ! = '/' ) )
continue ; // Not a full path match
}
filePath . Set ( fullPath ) ; // Matched!
}
}
else if ( var childFolder = entry as ProjectFolder )
{
FixFilePath ( filePath , childFolder ) ;
}
}
}
void FixSrcPath ( String fileName , String outFilePath )
{
outFilePath . Append ( fileName ) ;
IDEUtils . FixFilePath ( outFilePath ) ;
if ( File . Exists ( outFilePath ) )
return ;
if ( ! File . Exists ( outFilePath ) )
{
for ( var project in gApp . mWorkspace . mProjects )
{
FixFilePath ( outFilePath , project . mRootFolder ) ;
}
}
if ( ! File . Exists ( outFilePath ) )
{
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "Unable to locate project file '{0}'" , outFilePath ) ;
2019-08-23 11:56:54 -07:00
}
}
void FixFilePath ( String fileName , String outFilePath )
{
outFilePath . Append ( fileName ) ;
if ( File . Exists ( outFilePath ) )
return ;
outFilePath . Clear ( ) ;
Path . GetAbsolutePath ( fileName , gApp . mInstallDir , outFilePath ) ;
if ( ! File . Exists ( outFilePath ) )
{
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "Unable to locate file '{0}'" , outFilePath ) ;
2019-08-23 11:56:54 -07:00
}
}
SourceViewPanel GetActiveSourceViewPanel ( )
{
var sourceViewPanel = gApp . GetActiveSourceViewPanel ( ) ;
if ( sourceViewPanel = = null )
{
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "No active source view panel" ) ;
2019-08-23 11:56:54 -07:00
return null ;
}
sourceViewPanel . EnsureReady ( ) ;
return sourceViewPanel ;
}
TextPanel GetActiveTextPanel ( )
{
var textPanel = gApp . GetActivePanel ( ) as TextPanel ;
if ( textPanel = = null )
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "No active text panel" ) ;
2019-08-23 11:56:54 -07:00
return textPanel ;
}
public bool Evaluate ( String evalStr , String outVal , DebugManager . EvalExpressionFlags expressionFlags = . AllowCalls | . AllowSideEffects )
{
2019-08-27 08:04:41 -07:00
var curCmd = ScriptManager . sActiveManager . mCurCmd ;
2019-08-23 11:56:54 -07:00
if ( curCmd . mIntParam = = 1 ) // Pending
{
gApp . mDebugger . EvaluateContinue ( outVal ) ;
if ( outVal . StartsWith ( "!Not paused" ) )
{
curCmd . mHandled = false ;
return false ;
}
}
else
{
gApp . mDebugger . Evaluate ( evalStr , outVal , - 1 , ( int ) - 1 , expressionFlags ) ;
}
if ( outVal . StartsWith ( "!pending" ) )
{
curCmd . mIntParam = 1 ;
curCmd . mHandled = false ;
return false ;
}
if ( outVal . StartsWith ( "!" ) )
{
int tabPos = outVal . LastIndexOf ( '\t' ) ;
if ( tabPos ! = - 1 )
{
outVal . Remove ( 0 , tabPos + 1 ) ;
outVal . Insert ( 0 , "ERROR:'" ) ;
outVal . Append ( "'" ) ;
}
return true ;
}
if ( expressionFlags . HasFlag ( . MemoryWatch ) )
return true ;
int splitPos = outVal . IndexOf ( '\n' ) ;
if ( splitPos ! = - 1 )
outVal . RemoveToEnd ( splitPos ) ;
//int tabPos = outVal.IndexOf('\t');
//splitPos = (int)Math.Min((uint)tabPos, (uint)splitPos);
return true ;
}
[IDECommand]
public void OutputLine ( Object obj )
{
gApp . OutputLine ( "SCRIPT: {0}" , obj ) ;
}
[IDECommand]
public void Assert ( bool val )
{
if ( ! val )
{
gApp . OutputLine ( "SCRIPT ASSERT FAILED" ) ;
}
}
[IDECommand]
public void Sleep ( int length )
{
int wantTicks = length * gApp . RefreshRate / 1000 ;
2019-08-27 08:04:41 -07:00
var curCmd = ScriptManager . sActiveManager . mCurCmd ;
2019-08-23 11:56:54 -07:00
if ( ( + + curCmd . mIntParam < = wantTicks ) | | ( length < 0 ) ) // Negative is forever
curCmd . mHandled = false ;
}
[IDECommand]
public void SleepTicks ( int length )
{
int wantTicks = length ;
2019-08-27 08:04:41 -07:00
var curCmd = ScriptManager . sActiveManager . mCurCmd ;
2019-08-23 11:56:54 -07:00
if ( ( + + curCmd . mIntParam < = wantTicks ) | | ( length < 0 ) ) // Negative is forever
curCmd . mHandled = false ;
}
bool mIsFirstBreak = true ;
public bool IsPaused ( )
{
if ( gApp . mLastActiveSourceViewPanel ! = null )
{
var sourceViewPanel = gApp . mLastActiveSourceViewPanel ;
2019-09-04 07:00:38 -07:00
if ( sourceViewPanel . HasFocus ( ) )
{
if ( sourceViewPanel . [ Friend ] mOldVerLoadExecutionInstance ! = null )
return false ;
if ( ! sourceViewPanel . mDeferredResolveResults . IsEmpty )
return false ;
if ( sourceViewPanel . [ Friend ] mWantsFastClassify )
return false ;
if ( sourceViewPanel . [ Friend ] mWantsFullClassify )
return false ;
if ( sourceViewPanel . [ Friend ] mWantsFullRefresh )
return false ;
}
2019-08-23 11:56:54 -07:00
}
2019-08-29 14:19:07 -07:00
if ( ( gApp . mBfResolveCompiler ! = null ) & & ( gApp . mBfResolveCompiler . IsPerformingBackgroundOperation ( ) ) )
2019-08-23 11:56:54 -07:00
return false ;
if ( gApp . [ Friend ] mDeferredOpen ! = . None )
return false ;
2019-08-27 08:04:41 -07:00
if ( ( gApp . mExecutionPaused ) & & ( gApp . mDebugger . IsPaused ( ) ) )
{
if ( gApp . mWantsRehupCallstack )
return false ;
}
2019-08-23 11:56:54 -07:00
if ( gApp . mWantsClean | | gApp . mWantsBeefClean )
return false ;
2019-08-27 08:04:41 -07:00
if ( gApp . IsCompiling )
{
if ( ! ScriptManager . sActiveManager . mAllowCompiling )
return false ;
}
if ( ! gApp . [ Friend ] mExecutionInstances . IsEmpty )
return false ;
2019-08-29 14:19:07 -07:00
if ( gApp . mDebugger = = null )
return true ;
2019-08-27 08:04:41 -07:00
if ( ( ! gApp . AreTestsRunning ( ) ) & & ( ! gApp . mDebugger . HasPendingDebugLoads ( ) ) & &
2019-08-23 11:56:54 -07:00
( ( gApp . mExecutionPaused ) | | ( ! gApp . mDebugger . mIsRunning ) ) )
{
var runState = gApp . mDebugger . GetRunState ( ) ;
if ( runState = = . Terminating )
{
return false ;
}
if ( runState = = . SearchingSymSrv )
{
return false ;
}
if ( runState = = . DebugEval )
{
return false ;
}
2019-08-29 14:19:07 -07:00
if ( runState = = . Running_ToTempBreakpoint )
return false ;
2019-08-23 11:56:54 -07:00
Debug . Assert ( ( runState = = . NotStarted ) | | ( runState = = . Paused ) | | ( runState = = . Running_ToTempBreakpoint ) | |
( runState = = . Exception ) | | ( runState = = . Breakpoint ) | | ( runState = = . Terminated ) ) ;
/ * if ( runState = = . Paused )
{
NOP ! ( ) ;
}
else if ( ( runState = = . Paused ) | | ( runState = = . Exception ) | | ( runState = = . Breakpoint ) ) * /
{
if ( ( runState ! = . NotStarted ) & & ( mIsFirstBreak ) )
{
//Debug.Assert((runState == .Breakpoint) || (gApp.IsCrashDump));
mIsFirstBreak = false ;
}
//TEMPORARY TEST:
//Debug.Assert(runState == .Breakpoint);
return true ;
}
}
return false ;
}
[IDECommand]
public void WaitForPaused ( )
{
2019-08-27 08:04:41 -07:00
var curCmd = ScriptManager . sActiveManager . mCurCmd ;
2019-08-23 11:56:54 -07:00
curCmd . mHandled = IsPaused ( ) ;
}
[IDECommand]
public void WaitForResolve ( )
{
2019-08-27 08:04:41 -07:00
var curCmd = ScriptManager . sActiveManager . mCurCmd ;
2019-08-23 11:56:54 -07:00
curCmd . mHandled = IsPaused ( ) & & ( ! gApp . mBfResolveCompiler . IsPerformingBackgroundOperation ( ) ) ;
}
[IDECommand]
public void OpenCrashDump ( String fileName )
{
String filePath = scope String ( ) ;
FixFilePath ( fileName , filePath ) ;
gApp . OpenCrashDump ( filePath ) ;
}
[IDECommand]
public void SetSymSrvOptions ( String symCacheDir , String symSrvStr , String flagsStr )
{
switch ( Enum . Parse < DebugManager . SymSrvFlags > ( flagsStr ) )
{
case . Ok ( let flags ) :
gApp . mDebugger . SetSymSrvOptions ( symCacheDir , symSrvStr , flags ) ;
case . Err :
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "Failed to parse flags" ) ;
2019-08-23 11:56:54 -07:00
}
}
[IDECommand]
public void ShowFile ( String fileName )
{
String filePath = scope String ( ) ;
FixSrcPath ( fileName , filePath ) ;
gApp . ShowSourceFile ( filePath ) ;
}
[IDECommand]
public void DelTree ( String dirPath )
{
if ( Utils . DelTree ( dirPath ) case . Err )
{
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( scope String ( ) . . AppendF ( "Failed to deltree '{}'" , dirPath ) ) ;
2019-08-23 11:56:54 -07:00
}
}
[IDECommand]
public void CreateFile ( String path , String text )
{
let fileStream = scope FileStream ( ) ;
if ( fileStream . Create ( path ) case . Err )
{
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "Failed to create file '{}'" , path ) ;
2019-08-23 11:56:54 -07:00
return ;
}
fileStream . Write ( text ) ;
}
[IDECommand]
public void RenameFile ( String origPath , String newPath )
{
if ( File . Move ( origPath , newPath ) case . Err )
{
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "Failed to move file '{}' to '{}'" , origPath , newPath ) ;
2019-08-23 11:56:54 -07:00
}
}
[IDECommand]
public void DeleteFile ( String path )
{
if ( File . Delete ( path ) case . Err )
{
2019-08-27 08:04:41 -07:00
mScriptManager . Fail ( "Failed to delete file '{}'" , path ) ;
2019-08-23 11:56:54 -07:00
}
}
2019-08-27 08:04:41 -07:00
/ * [ IDECommand ]
public void Copy ( String srcPath , String destPath )
{
} * /
[IDECommand]
public void CopyFilesIfNewer ( String srcPath , String destPath )
{
int copyCount = 0 ;
int foundCount = 0 ;
void Do ( String srcPath , String destPath )
{
bool checkedDestDir = false ;
for ( var entry in Directory . Enumerate ( srcPath , . Directories | . Files ) )
{
foundCount + + ;
if ( mScriptManager . mFailed )
return ;
String srcFilePath = scope . ( ) ;
entry . GetFilePath ( srcFilePath ) ;
String srcFileName = scope . ( ) ;
entry . GetFileName ( srcFileName ) ;
String destFilePath = scope . ( ) ;
Path . GetAbsolutePath ( srcFileName , destPath , destFilePath ) ;
if ( entry . IsDirectory )
{
Do ( srcFilePath , destFilePath ) ;
continue ;
}
DateTime srcDate ;
if ( ! ( File . GetLastWriteTime ( srcFilePath ) case . Ok ( out srcDate ) ) )
continue ;
bool wantCopy = true ;
if ( File . GetLastWriteTime ( destFilePath ) case . Ok ( let destDate ) )
{
wantCopy = srcDate > destDate ;
}
if ( ! wantCopy )
continue ;
if ( ! checkedDestDir )
{
if ( Directory . CreateDirectory ( destPath ) case . Err )
{
mScriptManager . Fail ( "Failed to create directory '{}'" , destPath ) ;
return ;
}
}
if ( File . Copy ( srcFilePath , destFilePath ) case . Err )
{
mScriptManager . Fail ( "Failed to copy '{}' to '{}'" , srcFilePath , destFilePath ) ;
return ;
}
copyCount + + ;
}
}
Do ( srcPath , destPath ) ;
if ( foundCount = = 0 )
{
String srcDirPath = scope . ( ) ;
Path . GetDirectoryPath ( srcPath , srcDirPath ) ;
if ( ! Directory . Exists ( srcDirPath ) )
{
mScriptManager . Fail ( "Source directory does not exist: {}" , srcDirPath ) ;
}
else if ( ( ! srcDirPath . Contains ( '*' ) ) & & ( ! srcDirPath . Contains ( '?' ) ) & & ( ! File . Exists ( srcDirPath ) ) )
{
mScriptManager . Fail ( "Source file does not exist: {}" , srcPath ) ;
}
}
if ( ( ! mScriptManager . mFailed ) & & ( copyCount > 0 ) & & ( mScriptManager . mVerbosity > = . Normal ) )
{
if ( mScriptManager . mCurCmd . mStopWatch ! = null )
{
mScriptManager . mCurCmd . mStopWatch . Stop ( ) ;
gApp . OutputLine ( "{} files copied from '{}' to '{}' in {:0.00}s" , foundCount , srcPath , destPath , mScriptManager . mCurCmd . mStopWatch . ElapsedMilliseconds / 1000.0f ) ;
DeleteAndNullify ! ( mScriptManager . mCurCmd . mStopWatch ) ;
}
else
gApp . OutputLine ( "{} files copied from '{}' to '{}'" , foundCount , srcPath , destPath ) ;
}
}
2019-08-29 14:19:07 -07:00
public Project GetProject ( )
{
if ( mScriptManager . mProjectName = = null )
{
mScriptManager . Fail ( "Only usable in the context of a project" ) ;
return null ;
}
let project = gApp . mWorkspace . FindProject ( mScriptManager . mProjectName ) ;
if ( project = = null )
{
mScriptManager . Fail ( "Unable to find project '{}'" , mScriptManager . mProjectName ) ;
return null ;
}
return project ;
}
[IDECommand]
public void CopyToDependents ( String srcPath )
{
let depProject = GetProject ( ) ;
if ( depProject = = null )
return ;
for ( let checkProject in gApp . mWorkspace . mProjects )
{
if ( checkProject . HasDependency ( depProject . mProjectName ) )
{
List < String > targetPaths = scope . ( ) ;
defer ClearAndDeleteItems ( targetPaths ) ;
let workspaceOptions = gApp . GetCurWorkspaceOptions ( ) ;
let options = gApp . GetCurProjectOptions ( checkProject ) ;
gApp . [ Friend ] GetTargetPaths ( checkProject , workspaceOptions , options , targetPaths ) ;
if ( ! targetPaths . IsEmpty )
{
String targetDirPath = scope . ( ) ;
Path . GetDirectoryPath ( targetPaths [ 0 ] , targetDirPath ) ;
2019-09-18 13:01:29 -07:00
bool CopyFile ( String srcPath )
{
String fileName = scope . ( ) ;
Path . GetFileName ( srcPath , fileName ) ;
String destPath = scope . ( ) ;
Path . GetAbsolutePath ( fileName , targetDirPath , destPath ) ;
if ( File . CopyIfNewer ( srcPath , destPath ) case . Err )
{
mScriptManager . Fail ( "Failed to copy file '{}' to '{}'" , srcPath , destPath ) ;
return false ;
}
return true ;
}
2019-08-29 14:19:07 -07:00
2019-09-18 13:01:29 -07:00
if ( srcPath . Contains ( '*' ) )
2019-08-29 14:19:07 -07:00
{
2019-09-18 13:01:29 -07:00
String dirPath = scope . ( ) ;
String wildcard = scope . ( ) ;
Path . GetDirectoryPath ( srcPath , dirPath ) ;
Path . GetFileName ( srcPath , wildcard ) ;
for ( let entry in Directory . EnumerateFiles ( dirPath , wildcard ) )
{
String foundPath = scope . ( ) ;
entry . GetFilePath ( foundPath ) ;
if ( ! CopyFile ( foundPath ) )
return ;
}
}
else
{
if ( ! CopyFile ( srcPath ) )
return ;
2019-08-29 14:19:07 -07:00
}
}
}
}
}
2019-08-27 08:04:41 -07:00
[IDECommand]
public void ExecuteRaw ( String cmd )
{
var exePath = scope String ( ) ;
int spacePos ;
if ( cmd . StartsWith ( "\"" ) )
{
spacePos = cmd . IndexOf ( '"' , 1 ) + 1 ;
if ( spacePos ! = - 1 )
exePath . Append ( cmd , 1 , spacePos - 2 ) ;
}
else
{
spacePos = cmd . IndexOf ( ' ' ) ;
if ( spacePos ! = - 1 )
exePath . Append ( cmd , 0 , spacePos ) ;
}
if ( ( spacePos = = - 1 ) & & ( ! cmd . IsEmpty ) )
{
mScriptManager . Fail ( "Invalid command '{0}' in '{1}'" , cmd , mScriptManager . mCurCmd . mSrcFile ) ;
return ;
}
if ( spacePos > 0 )
{
var exeArgs = scope String ( ) ;
exeArgs . Append ( cmd , spacePos + 1 ) ;
gApp . DoRun ( exePath , exeArgs , gApp . mInstallDir , . None ) ;
}
}
[IDECommand]
public void Execute ( String path )
{
ExecuteRaw ( path ) ;
}
2019-08-23 11:56:54 -07:00
[IDECommand]
public void SetFileWatcherDelay ( int32 delay )
{
FileWatcher . sDbgFileCreateDelay = delay ;
}
[IDECommand]
public void RenameFile_TempRenameDelete ( String origPath , String newPath )
{
String content = scope . ( ) ;
if ( File . ReadAllText ( origPath , content , true ) case . Err )
{
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "Failed to open file '{}'" , origPath ) ;
2019-08-23 11:56:54 -07:00
return ;
}
String tempPath = scope . ( ) ;
while ( true )
{
tempPath . Clear ( ) ;
Path . GetDirectoryPath ( origPath , tempPath ) ;
tempPath . Append ( Path . DirectorySeparatorChar ) ;
tempPath . Append ( "_" ) ;
Rand . Int ( ) . ToString ( tempPath ) ;
tempPath . Append ( ".tmp" ) ;
if ( ! File . Exists ( tempPath ) )
break ;
}
FileStream tempStream = scope . ( ) ;
if ( tempStream . Create ( tempPath ) case . Err )
{
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "Failed to create temp file '{}'" , tempPath ) ;
2019-08-23 11:56:54 -07:00
return ;
}
tempStream . Write ( content ) ;
tempStream . Close ( ) ;
if ( File . Move ( tempPath , newPath ) case . Err )
{
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "Failed to move file '{}' to '{}'" , origPath , newPath ) ;
2019-08-23 11:56:54 -07:00
return ;
}
if ( File . Delete ( origPath ) case . Err )
{
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "Failed to delete file '{}'" , origPath ) ;
2019-08-23 11:56:54 -07:00
return ;
}
}
[IDECommand]
public void OpenWorkspace ( String dirPath )
{
gApp . [ Friend ] mDeferredOpen = . Workspace ;
var selectedPath = scope String . . AppendF ( dirPath ) ;
selectedPath . Append ( Path . DirectorySeparatorChar ) ;
selectedPath . Append ( "BeefSpace.toml" ) ;
IDEUtils . FixFilePath ( selectedPath ) ;
gApp . [ Friend ] mDeferredOpenFileName = new String ( selectedPath ) ;
}
[IDECommand]
public void ClickPanelButton ( String buttonName )
{
var sourceViewPanel = GetActiveSourceViewPanel ( ) ;
if ( sourceViewPanel = = null )
return ;
var panelHeader = sourceViewPanel . [ Friend ] mPanelHeader ;
if ( panelHeader = = null )
{
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "No panel present" ) ;
2019-08-23 11:56:54 -07:00
return ;
}
for ( var widget in panelHeader . mChildWidgets )
{
if ( var button = widget as DarkButton )
{
if ( button . Label = = buttonName )
{
button . MouseClicked ( 0 , 0 , 0 ) ;
return ;
}
}
}
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "Button '{0}' not found" , buttonName ) ;
2019-08-23 11:56:54 -07:00
}
[IDECommand]
public void SelectLine ( int lineNum )
{
var sourceViewPanel = GetActiveSourceViewPanel ( ) ;
if ( sourceViewPanel ! = null )
{
sourceViewPanel . ShowFileLocation ( - 1 , lineNum - 1 , 0 , . None ) ;
}
}
[IDECommand]
public void AssertEvalEquals ( String evalStr , String evalResult )
{
String outVal = scope String ( ) ;
if ( ! Evaluate ( evalStr , outVal ) )
return ;
if ( outVal ! = evalResult )
{
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "Assert failed: {0} == {1}" , outVal , evalResult ) ;
2019-08-23 11:56:54 -07:00
}
}
[IDECommand]
public void AssertEvalContains ( String evalStr , String evalResult )
{
String outVal = scope String ( ) ;
if ( ! Evaluate ( evalStr , outVal ) )
return ;
if ( ! outVal . Contains ( evalResult ) )
{
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "Assert failed: {0} contains {1}" , outVal , evalResult ) ;
2019-08-23 11:56:54 -07:00
}
}
[IDECommand]
public void ImmediateEvaluate ( String evalStr )
{
gApp . ShowImmediatePanel ( ) ;
gApp . mImmediatePanel . [ Friend ] mImmediateWidget . mEditWidgetContent . InsertAtCursor ( evalStr ) ;
gApp . mImmediatePanel . [ Friend ] mImmediateWidget . KeyChar ( '\n' ) ;
}
[IDECommand]
public void SelectCallStackIdx ( int selectIdx )
{
while ( true )
{
int stackCount = gApp . mDebugger . GetCallStackCount ( ) ;
if ( selectIdx < stackCount )
break ;
gApp . mDebugger . UpdateCallStack ( ) ;
if ( stackCount = = gApp . mDebugger . GetCallStackCount ( ) )
{
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "Stack idx '{0}' is out of range" , selectIdx ) ;
2019-08-23 11:56:54 -07:00
}
}
gApp . mDebugger . mActiveCallStackIdx = ( . ) selectIdx ;
}
[IDECommand]
public void SelectCallStackWithStr ( String str )
{
int32 stackIdx = 0 ;
while ( true )
{
int stackCount = gApp . mDebugger . GetCallStackCount ( ) ;
if ( stackIdx > = stackCount )
{
gApp . mDebugger . UpdateCallStack ( ) ;
if ( stackIdx > = gApp . mDebugger . GetCallStackCount ( ) )
break ;
}
String file = scope . ( ) ;
String stackFrameInfo = scope . ( ) ;
gApp . mDebugger . GetStackFrameInfo ( stackIdx , var addr , file , stackFrameInfo ) ;
if ( stackFrameInfo . Contains ( str ) )
{
gApp . mDebugger . mActiveCallStackIdx = ( . ) stackIdx ;
return ;
}
stackIdx + + ;
}
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "Failed to find stack frame containing string '{}'" , str ) ;
2019-08-23 11:56:54 -07:00
}
2019-08-29 14:19:07 -07:00
public bool AssertRunning ( )
{
if ( ! gApp . mDebugger . mIsRunning )
{
mScriptManager . Fail ( "Expected target to be running" ) ;
return false ;
}
return true ;
}
public bool AssertDebuggerPaused ( )
{
if ( ! gApp . mDebugger . mIsRunning )
{
mScriptManager . Fail ( "Expected target to be running" ) ;
return false ;
}
if ( ! gApp . mDebugger . IsPaused ( ) )
{
mScriptManager . Fail ( "Expected target to be paused" ) ;
return false ;
}
return true ;
}
[IDECommand]
public void StepInto ( )
{
if ( ! AssertDebuggerPaused ( ) )
return ;
gApp . [ Friend ] StepInto ( ) ;
}
[IDECommand]
public void StepOver ( )
{
if ( ! AssertDebuggerPaused ( ) )
return ;
gApp . [ Friend ] StepOver ( ) ;
}
2019-08-23 11:56:54 -07:00
[IDECommand]
public void SelectThread ( String threadName )
{
String threadInfo = scope . ( ) ;
gApp . mDebugger . GetThreadInfo ( threadInfo ) ;
for ( var infoLine in threadInfo . Split ( '\n' ) )
{
if ( @infoLine . Pos = = 0 )
continue ;
var infoSections = infoLine . Split ( '\t' ) ;
StringView id = infoSections . GetNext ( ) . GetValueOrDefault ( ) ;
StringView name = infoSections . GetNext ( ) . GetValueOrDefault ( ) ;
if ( ( threadName . IsEmpty ) | | ( threadName = = name ) | | ( threadName = = id ) )
{
gApp . mDebugger . SetActiveThread ( int32 . Parse ( id ) . GetValueOrDefault ( ) ) ;
break ;
}
}
}
[IDECommand]
public void AssertCurrentMethod ( String methodName )
{
int addr ;
String fileName = scope String ( ) ;
String stackframeInfo = scope String ( ) ;
gApp . mDebugger . GetStackFrameInfo ( gApp . mDebugger . mActiveCallStackIdx , out addr , fileName , stackframeInfo ) ;
if ( methodName ! = stackframeInfo )
{
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "Expect method name '{0}', got '{1}'" , methodName , stackframeInfo ) ;
2019-08-23 11:56:54 -07:00
}
}
[IDECommand]
public void CreateMemoryBreakpoint ( String evalStr )
{
String outVal = scope String ( ) ;
if ( ! Evaluate ( evalStr , outVal , . AllowCalls | . AllowSideEffects | . MemoryWatch ) )
return ;
var vals = scope List < StringView > ( outVal . Split ( '\n' ) ) ;
int64 addr = ( int ) int64 . Parse ( scope String ( vals [ 0 ] ) , System . Globalization . NumberStyles . HexNumber ) ;
int32 byteCount = int32 . Parse ( scope String ( vals [ 1 ] ) ) ;
String addrType = scope String ( vals [ 2 ] ) ;
# unwarn
var breakpoint = gApp . mDebugger . CreateMemoryBreakpoint ( evalStr , addr , byteCount , addrType ) ;
}
[IDECommand]
public void Crash ( )
{
int * a = null ;
* a = 123 ;
}
[IDECommand]
public void Leak ( )
{
new String ( "This string leaked" ) ;
}
[IDECommand]
public void BreakpointSetCondition ( String condition )
{
var lastBreakpoint = gApp . mDebugger . mBreakpointList . Back ;
if ( lastBreakpoint = = null )
{
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "No last breakpoint" ) ;
2019-08-23 11:56:54 -07:00
return ;
}
lastBreakpoint . SetCondition ( condition ) ;
}
[IDECommand]
public void BreakpointSetHitCountTarget ( int hitCountTarget , String hitCountBreakKindStr )
{
var lastBreakpoint = gApp . mDebugger . mBreakpointList . Back ;
if ( lastBreakpoint = = null )
{
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "No last breakpoint" ) ;
2019-08-23 11:56:54 -07:00
return ;
}
switch ( Enum . Parse < Breakpoint . HitCountBreakKind > ( hitCountBreakKindStr ) )
{
case . Err :
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "Invalid break kind: '{0}'" , hitCountBreakKindStr ) ;
2019-08-23 11:56:54 -07:00
case . Ok ( let hitCountBreakKind ) :
lastBreakpoint . SetHitCountTarget ( hitCountTarget , hitCountBreakKind ) ;
}
}
[IDECommand]
public void InsertImmediateText ( String text )
{
gApp . ShowImmediatePanel ( ) ;
var ewc = ( SourceEditWidgetContent ) gApp . mImmediatePanel . EditWidget . mEditWidgetContent ;
ewc . InsertAtCursor ( text ) ;
gApp . mImmediatePanel . Update ( ) ;
ewc . mOnGenerateAutocomplete ( 0 , . UserRequested ) ;
}
AutoComplete GetAutocomplete ( )
{
var textPanel = GetActiveTextPanel ( ) ;
if ( textPanel = = null )
{
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "No text panel active" ) ;
2019-08-23 11:56:54 -07:00
return null ;
}
var ewc = textPanel . EditWidget . mEditWidgetContent as SourceEditWidgetContent ;
if ( ewc = = null )
{
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "Not an autocomplete text view" ) ;
2019-08-23 11:56:54 -07:00
return null ;
}
if ( ewc . mAutoComplete = = null )
{
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "No autocomplete content" ) ;
2019-08-23 11:56:54 -07:00
return null ;
}
return ewc . mAutoComplete ;
}
[IDECommand]
public bool AssertAutocompleteEntry ( StringView wantEntry )
{
var wantEntry ;
bool wantsFind = true ;
if ( wantEntry . StartsWith ( "!" ) )
{
wantsFind = false ;
wantEntry . RemoveFromStart ( 1 ) ;
}
var autoComplete = GetAutocomplete ( ) ;
if ( autoComplete = = null )
{
return false ;
}
bool found = false ;
if ( autoComplete . mAutoCompleteListWidget ! = null )
{
for ( var entry in autoComplete . mAutoCompleteListWidget . mEntryList )
{
if ( entry . mEntryDisplay = = wantEntry )
found = true ;
}
}
if ( found ! = wantsFind )
{
if ( wantsFind )
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "Autocomplete entry '{0}' not found" , wantEntry ) ;
2019-08-23 11:56:54 -07:00
else
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "Autocomplete entry '{0}' found, but it shouldn't have been" , wantEntry ) ;
2019-08-23 11:56:54 -07:00
return false ;
}
return true ;
}
[IDECommand]
public void AssertDbgAutocomplete ( String text )
{
var lastActivePanel = gApp . mLastActivePanel ;
InsertImmediateText ( text ) ;
for ( let entry in text . Split ( '\n' ) )
{
if ( ! entry . IsEmpty )
{
if ( ! AssertAutocompleteEntry ( entry ) )
break ;
}
}
ClearImmediate ( ) ;
gApp . mLastActivePanel = lastActivePanel ;
}
[IDECommand]
public bool AssertAutocompleteEquals ( String insertText , String wantsContents )
{
InsertImmediateText ( insertText ) ;
var autoComplete = GetAutocomplete ( ) ;
if ( autoComplete = = null )
{
return false ;
}
String contents = scope . ( ) ;
if ( autoComplete . mAutoCompleteListWidget ! = null )
{
for ( var entry in autoComplete . mAutoCompleteListWidget . mEntryList )
{
if ( ! contents . IsEmpty )
contents . Append ( "\n" ) ;
contents . Append ( entry . mEntryDisplay ) ;
}
}
ClearImmediate ( ) ;
if ( contents ! = wantsContents )
{
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "Autocomplete not showing expected values. Expected '{}', got '{}'." , wantsContents , contents ) ;
2019-08-23 11:56:54 -07:00
return false ;
}
return true ;
}
[IDECommand]
public void CloseAutocomplete ( )
{
var autoComplete = GetAutocomplete ( ) ;
if ( autoComplete = = null )
return ;
autoComplete . Close ( ) ;
}
[IDECommand]
public void ClearImmediate ( )
{
gApp . mImmediatePanel . Clear ( ) ;
}
[IDECommand]
public void AssertIsAt ( String fileName , int lineNum )
{
String filePath = scope String ( ) ;
FixSrcPath ( fileName , filePath ) ;
var sourceViewPanel = GetActiveSourceViewPanel ( ) ;
if ( sourceViewPanel = = null )
return ;
if ( ! Path . Equals ( filePath , sourceViewPanel . mFilePath ) )
{
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "Expected source file '{0}', got '{1}'" , filePath , sourceViewPanel . mFilePath ) ;
2019-08-23 11:56:54 -07:00
return ;
}
let atLine = sourceViewPanel . mEditWidget . mEditWidgetContent . CursorLineAndColumn . mLine + 1 ;
if ( atLine ! = lineNum )
{
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "Expected line '{0}', got '{1}'" , lineNum , atLine ) ;
2019-08-23 11:56:54 -07:00
return ;
}
}
[IDECommand]
public void GotoTextSkip ( String findText , int skipIdx )
{
var skipIdx ;
var textPanel = GetActiveTextPanel ( ) ;
if ( textPanel = = null )
{
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "No active text panel" ) ;
2019-08-23 11:56:54 -07:00
return ;
}
var ewc = textPanel . EditWidget . mEditWidgetContent ;
for ( int32 startIdx < ewc . mData . mTextLength )
{
bool isEqual = true ;
for ( int32 i = 0 ; i < findText . Length ; i + + )
{
if ( ewc . mData . mText [ i + startIdx ] . mChar ! = findText [ i ] )
{
isEqual = false ;
break ;
}
}
if ( isEqual )
{
if ( skipIdx > 0 )
{
skipIdx - - ;
}
else
{
ewc . CursorTextPos = startIdx ;
return ;
}
}
}
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "Unable to find text '{0}'" , findText ) ;
2019-08-23 11:56:54 -07:00
}
[IDECommand]
public void GotoText ( String findText )
{
GotoTextSkip ( findText , 0 ) ;
}
[IDECommand]
public void AssertLineContains ( String findText )
{
var textPanel = GetActiveTextPanel ( ) ;
if ( textPanel = = null )
return ;
var ewc = textPanel . EditWidget . mEditWidgetContent ;
int line = ewc . CursorLineAndColumn . mLine ;
var lineText = scope String ( ) ;
ewc . GetLineText ( line , lineText ) ;
if ( ! lineText . Contains ( findText ) )
{
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "Lines does not contain text '{0}'" , findText ) ;
2019-08-23 11:56:54 -07:00
}
}
[IDECommand]
public void MoveCursor ( int line , int column )
{
var textPanel = GetActiveTextPanel ( ) ;
if ( textPanel = = null )
return ;
var ewc = textPanel . EditWidget . mEditWidgetContent ;
ewc . CursorLineAndColumn = . ( line , column ) ;
}
[IDECommand]
public void AdjustCursor ( int relColumn , int relLine )
{
var textPanel = GetActiveTextPanel ( ) ;
if ( textPanel = = null )
return ;
var ewc = textPanel . EditWidget . mEditWidgetContent ;
var cursorPos = ewc . CursorLineAndColumn ;
cursorPos . mLine + = ( . ) relLine ;
cursorPos . mColumn + = ( . ) relColumn ;
ewc . CursorLineAndColumn = cursorPos ;
}
[IDECommand]
public void InsertText ( String text )
{
var textPanel = GetActiveTextPanel ( ) ;
if ( textPanel = = null )
return ;
textPanel . EditWidget . mEditWidgetContent . InsertAtCursor ( text ) ;
}
[IDECommand]
public void DeleteText ( )
{
var textPanel = GetActiveTextPanel ( ) ;
if ( textPanel = = null )
return ;
textPanel . EditWidget . mEditWidgetContent . DeleteSelection ( ) ;
}
[IDECommand]
public void MarkPosition ( )
{
var textPanel = GetActiveTextPanel ( ) ;
if ( textPanel = = null )
return ;
var ewc = textPanel . EditWidget . mEditWidgetContent ;
mMarkedPos = ewc . CursorLineAndColumn ;
}
[IDECommand]
public void SelectToMark ( )
{
var textPanel = GetActiveTextPanel ( ) ;
if ( textPanel = = null )
return ;
var ewc = textPanel . EditWidget . mEditWidgetContent ;
var prevPos = ewc . CursorLineAndColumn ;
EditSelection sel ;
ewc . CursorLineAndColumn = mMarkedPos ;
sel . mStartPos = ( . ) ewc . CursorTextPos ;
ewc . CursorLineAndColumn = prevPos ;
sel . mEndPos = ( . ) ewc . CursorTextPos ;
ewc . mSelection = sel ;
}
[IDECommand]
public void RemoveSelection ( )
{
var textPanel = GetActiveTextPanel ( ) ;
if ( textPanel = = null )
return ;
var ewc = textPanel . EditWidget . mEditWidgetContent ;
ewc . mSelection = null ;
}
[IDECommand]
public void ToggleCommentBetween ( String textFrom , String textTo )
{
GotoText ( textFrom ) ;
AdjustCursor ( 1 , 0 ) ;
MarkPosition ( ) ;
GotoText ( textTo ) ;
SelectToMark ( ) ;
gApp . [ Friend ] ToggleComment ( ) ;
}
[IDECommand]
public void AssertCompileSucceeded ( )
{
if ( gApp . IsCompiling )
{
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . mCurCmd . mHandled = false ;
2019-08-23 11:56:54 -07:00
return ;
}
if ( gApp . mLastCompileFailed )
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "Compile failed" ) ;
2019-08-23 11:56:54 -07:00
}
[IDECommand]
public void ToggleCommentAt ( String textFrom )
{
var sourceViewPanel = GetActiveSourceViewPanel ( ) ;
if ( sourceViewPanel = = null )
return ;
var ewc = sourceViewPanel . mEditWidget . mEditWidgetContent ;
GotoText ( textFrom ) ;
2019-08-27 08:04:41 -07:00
if ( ScriptManager . sActiveManager . Failed )
2019-08-23 11:56:54 -07:00
return ;
char8 nextC = 0 ;
int32 checkIdx = ( . ) ewc . CursorTextPos ;
while ( checkIdx > 0 )
{
if ( ewc . mData . mText [ checkIdx - 1 ] . mDisplayTypeId ! = ( . ) SourceElementType . Comment )
break ;
char8 c = ewc . mData . mText [ checkIdx - 1 ] . mChar ;
// If we have comments back-to-back then we need to do this...
if ( ( c = = '/' ) & & ( nextC = = '*' ) )
{
checkIdx - - ;
if ( checkIdx > 1 )
{
char8 prevC = ewc . mData . mText [ checkIdx - 1 ] . mChar ;
if ( prevC = = '/' )
checkIdx - - ;
}
break ;
}
nextC = c ;
checkIdx - - ;
}
int32 startPos = checkIdx ;
if ( ewc . mData . mText [ startPos + 1 ] . mChar = = '*' )
{
ewc . CursorTextPos = startPos ;
ewc . InsertAtCursor ( "/" ) ;
char8 prevC = 0 ;
int depth = 1 ;
checkIdx = ( . ) ewc . CursorTextPos + 2 ;
while ( checkIdx < ewc . mData . mTextLength )
{
char8 c = ewc . mData . mText [ checkIdx ] . mChar ;
if ( ewc . mData . mText [ checkIdx ] . mDisplayTypeId ! = ( . ) SourceElementType . Comment )
break ;
// If we have comments back-to-back then we need to do this...
if ( ( c = = '/' ) & & ( prevC = = '*' ) )
{
- - depth ;
if ( depth = = 0 )
{
checkIdx + + ;
break ;
}
}
else if ( ( c = = '*' ) & & ( prevC = = '/' ) )
depth + + ;
prevC = c ;
checkIdx + + ;
}
ewc . CursorTextPos = checkIdx - 2 ;
ewc . InsertAtCursor ( "/*@" ) ;
}
else
{
var text = ewc . mData . mText ;
ewc . mSelection = EditSelection ( startPos , startPos + 1 ) ;
ewc . DeleteSelection ( ) ;
checkIdx = ( . ) startPos ;
while ( checkIdx < ewc . mData . mTextLength - 4 )
{
if ( text [ checkIdx ] . mDisplayTypeId = = ( . ) SourceElementType . Comment )
{
if ( ( text [ checkIdx + 0 ] . mChar = = '/' ) & &
( text [ checkIdx + 1 ] . mChar = = '*' ) & &
( text [ checkIdx + 2 ] . mChar = = '@' ) & &
( text [ checkIdx + 3 ] . mChar = = '*' ) & &
( text [ checkIdx + 4 ] . mChar = = '/' ) )
{
ewc . mSelection = EditSelection ( checkIdx , checkIdx + 3 ) ;
ewc . DeleteSelection ( ) ;
break ;
}
}
checkIdx + + ;
}
}
}
[IDECommand]
public void SetExpectError ( String error )
{
2019-08-27 08:04:41 -07:00
DeleteAndNullify ! ( ScriptManager . sActiveManager . mExpectingError ) ;
ScriptManager . sActiveManager . mExpectingError = new String ( error ) ;
ScriptManager . sActiveManager . mHadExpectingError = true ;
2019-08-23 11:56:54 -07:00
}
[IDECommand]
public void ExpectError ( )
{
2019-08-27 08:04:41 -07:00
if ( ScriptManager . sActiveManager . mExpectingError ! = null )
2019-08-23 11:56:54 -07:00
{
2019-08-27 08:04:41 -07:00
DeleteAndNullify ! ( ScriptManager . sActiveManager . mExpectingError ) ;
ScriptManager . sActiveManager . Fail ( "Expected error did not occur" ) ;
2019-08-23 11:56:54 -07:00
}
}
[IDECommand]
public void AssertFileErrors ( )
{
var textPanel = GetActiveSourceViewPanel ( ) ;
if ( textPanel = = null )
{
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "No active text panel" ) ;
2019-08-23 11:56:54 -07:00
return ;
}
var ewc = textPanel . EditWidget . mEditWidgetContent ;
String lineText = scope String ( ) ;
for ( int lineIdx = 0 ; lineIdx < ewc . GetLineCount ( ) ; lineIdx + + )
{
lineText . Clear ( ) ;
ewc . GetLineText ( lineIdx , lineText ) ;
ewc . GetLinePosition ( lineIdx , var lineStart , var lineEnd ) ;
bool hasError = false ;
for ( int i = lineStart ; i < lineEnd ; i + + )
{
var flags = ( SourceElementFlags ) ewc . mData . mText [ i ] . mDisplayFlags ;
if ( flags . HasFlag ( . Error ) )
hasError = true ;
}
bool expectedError = lineText . Contains ( "//FAIL" ) ;
if ( hasError ! = expectedError )
{
if ( hasError )
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "Unexpected error at line {0} in {1}\n\t" , lineIdx + 1 , textPanel . mFilePath ) ;
2019-08-23 11:56:54 -07:00
else
2019-08-27 08:04:41 -07:00
ScriptManager . sActiveManager . Fail ( "Expected error at line {0} in {1} but didn't encounter one\n\t" , lineIdx + 1 , textPanel . mFilePath ) ;
2019-08-23 11:56:54 -07:00
return ;
}
}
}
[IDECommand]
public void Stop ( )
{
2019-08-29 14:19:07 -07:00
mScriptManager . Clear ( ) ;
2019-08-23 11:56:54 -07:00
}
[IDECommand]
public void Exit ( )
{
gApp . Stop ( ) ;
}
[IDECommand]
public void RestoreDebugFiles ( String dbgPath )
{
for ( var entry in Directory . EnumerateFiles ( dbgPath ) )
{
String dbgFilePath = scope . ( ) ;
entry . GetFilePath ( dbgFilePath ) ;
if ( ! dbgFilePath . EndsWith ( ".bf" ) )
continue ;
String dbgContent = scope . ( ) ;
File . ReadAllText ( dbgFilePath , dbgContent , false ) ;
if ( ! dbgContent . StartsWith ( "//@" ) )
continue ;
int barPos = dbgContent . IndexOf ( '|' ) ;
String srcPath = scope . ( ) ;
srcPath . Append ( dbgContent , 3 , barPos - 3 ) ;
int crPos = dbgContent . IndexOf ( '\n' ) ;
StringView dbgText = . ( dbgContent . Ptr + crPos + 1 , dbgContent . Length - crPos - 1 ) ;
let dbgHash = MD5 . Hash ( . ( ( uint8 * ) dbgText . Ptr , dbgText . Length ) ) ;
String srcContent = scope . ( ) ;
File . ReadAllText ( srcPath , srcContent , false ) ;
let srcHash = MD5 . Hash ( . ( ( uint8 * ) srcContent . Ptr , srcContent . Length ) ) ;
if ( dbgHash ! = srcHash )
{
String bkuPath = scope . ( ) ;
bkuPath . Append ( gApp . mInstallDir , "/bku/" ) ;
Path . GetFileNameWithoutExtension ( dbgFilePath , bkuPath ) ;
bkuPath . Append ( "_" ) ;
srcHash . ToString ( bkuPath ) ;
Path . GetExtension ( dbgFilePath , bkuPath ) ;
gApp . SafeWriteTextFile ( bkuPath , srcContent ) ;
gApp . SafeWriteTextFile ( srcPath , dbgText ) ;
}
}
}
[IDECommand]
public void RestoreDebugFilesSpan ( String dbgPath , int fromIdx , int endIdx )
{
for ( int i = fromIdx ; i < = endIdx ; i + + )
{
String versionedPath = scope . ( ) ;
versionedPath . AppendF ( "{}/{}/" , dbgPath , i ) ;
RestoreDebugFiles ( versionedPath ) ;
}
}
[IDECommand]
public void WaitDialog ( )
{
#if BF_PLATFORM_WINDOWS
Windows . MessageBoxA ( ( Windows . HWnd ) 0 , "Waiting for user input" , "Beef IDE" , 0 ) ;
#endif
}
}
}