LUAEditorContext.cpp 106 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AzCore/PlatformIncl.h>
  9. #include "LUAEditorContext.h"
  10. #include <AzCore/Asset/AssetManager.h>
  11. #include <AzCore/Component/ComponentApplicationBus.h>
  12. #include <AzCore/IO/FileIO.h>
  13. #include <AzCore/RTTI/BehaviorContext.h>
  14. #include <AzCore/Script/ScriptAsset.h>
  15. #include <AzCore/Script/ScriptContextAttributes.h>
  16. #include <AzCore/Script/ScriptSystemBus.h>
  17. #include <AzCore/Serialization/SerializeContext.h>
  18. #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
  19. #include <AzCore/std/string/conversions.h>
  20. #include <AzCore/std/string/regex.h>
  21. #include <AzCore/StringFunc/StringFunc.h>
  22. #include <AzFramework/Asset/AssetSystemBus.h>
  23. #include <AzFramework/Asset/AssetSystemComponent.h>
  24. #include <AzFramework/Asset/AssetCatalogBus.h>
  25. #include <AzFramework/Asset/AssetProcessorMessages.h>
  26. #include <AzFramework/Script/ScriptRemoteDebuggingConstants.h>
  27. #include <AzToolsFramework/API/EditorAssetSystemAPI.h>
  28. #include <AzToolsFramework/UI/UICore/SaveChangesDialog.hxx>
  29. #include <AzToolsFramework/UI/LegacyFramework/UIFrameworkAPI.h>
  30. #include <AzToolsFramework/UI/Logging/LogPanel_Panel.h>
  31. #include <AzToolsFramework/UI/UICore/QTreeViewStateSaver.hxx>
  32. #include <AzToolsFramework/SourceControl/SourceControlAPI.h>
  33. #include <Source/LUA/LUALocalsTrackerMessages.h>
  34. #include <QMessageBox>
  35. #include <regex>
  36. #include "LUAEditorContextInterface.h"
  37. #include "LUAEditorDebuggerMessages.h"
  38. #include "LUAWatchesDebuggerMessages.h"
  39. #include "LUAEditorViewMessages.h"
  40. #include "LUAContextControlMessages.h"
  41. namespace LUAEditor
  42. {
  43. const char* LUAEditorDebugName = "Lua Debug";
  44. const char* LUAEditorInfoName = "Lua Editor";
  45. LUAEditor::Context* s_pLUAEditorScriptPtr = nullptr; // for script access
  46. class BreakpointSavedState
  47. : public AZ::UserSettings
  48. {
  49. public:
  50. AZ_RTTI(BreakpointSavedState, "{EB3E0061-75AC-41F7-8631-6072F6C018EB}", AZ::UserSettings);
  51. AZ_CLASS_ALLOCATOR(BreakpointSavedState, AZ::SystemAllocator);
  52. BreakpointSavedState() {}
  53. BreakpointMap m_Breakpoints;
  54. static void Reflect(AZ::ReflectContext* reflection)
  55. {
  56. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection);
  57. if (serializeContext)
  58. {
  59. serializeContext->Class<BreakpointSavedState>()
  60. ->Field("m_Breakpoints", &BreakpointSavedState::m_Breakpoints)
  61. ->Version(2);
  62. }
  63. }
  64. };
  65. AZ::Uuid ContextID("37BA0B6A-CFCF-42CA-91A5-E794BB17AD6D");
  66. //////////////////////////////////////////////////////////////////////////
  67. //Context
  68. Context::Context()
  69. : m_pLUAEditorMainWindow(nullptr)
  70. , m_connectedState(false)
  71. , m_fileIO(nullptr)
  72. {
  73. m_referenceModel = new QStandardItemModel();
  74. m_numOutstandingOperations = 0;
  75. s_pLUAEditorScriptPtr = this;
  76. m_bProcessingActivate = false;
  77. m_bReloadCheckQueued = false;
  78. mostRecentlyOpenedDocumentView.clear();
  79. m_queuedOpenRecent = false;
  80. m_bShuttingDown = false;
  81. AddDefaultLUAKeywords();
  82. AddDefaultLUALibraryFunctions();
  83. }
  84. Context::~Context()
  85. {
  86. s_pLUAEditorScriptPtr = nullptr;
  87. delete m_referenceModel;
  88. for (auto errorData : m_errorData)
  89. {
  90. delete errorData;
  91. }
  92. m_errorData.clear();
  93. AZ_Assert(m_numOutstandingOperations == 0, "Save still pending when shut down.");
  94. AZ_Assert(!m_pLUAEditorMainWindow, "You must deactivate this Context");
  95. }
  96. Context::Context(const Context&)
  97. {
  98. AZ_Assert(false, "You can't copy this type!");
  99. }
  100. //////////////////////////////////////////////////////////////////////////
  101. // AZ::Component
  102. void Context::Init()
  103. {
  104. m_fileIO = AZ::IO::FileIOBase::GetInstance();
  105. AZ_Assert(m_fileIO, "FileIO system is not present, make sure a FileIO instance is set by the application.");
  106. auto* remoteToolsInterface = AzFramework::RemoteToolsInterface::Get();
  107. if (remoteToolsInterface)
  108. {
  109. m_connectedEventHandler = AzFramework::RemoteToolsEndpointConnectedEvent::Handler(
  110. [this](bool value)
  111. {
  112. this->DesiredTargetConnected(value);
  113. });
  114. remoteToolsInterface->RegisterRemoteToolsEndpointConnectedHandler(
  115. AzFramework::LuaToolsKey, m_connectedEventHandler);
  116. m_changedEventHandler = AzFramework::RemoteToolsEndpointChangedEvent::Handler(
  117. [this](AZ::u32 oldVal, AZ::u32 newVal)
  118. {
  119. this->DesiredTargetChanged(oldVal, newVal);
  120. });
  121. remoteToolsInterface->RegisterRemoteToolsEndpointChangedHandler(
  122. AzFramework::LuaToolsKey, m_changedEventHandler);
  123. }
  124. }
  125. void Context::Activate()
  126. {
  127. ResetTargetContexts();
  128. LegacyFramework::CoreMessageBus::Handler::BusConnect();
  129. ContextInterface::Handler::BusConnect(ContextID);
  130. Context_DocumentManagement::Handler::BusConnect();
  131. Context_DebuggerManagement::Handler::BusConnect();
  132. LUABreakpointRequestMessages::Handler::BusConnect();
  133. LUAStackRequestMessages::Handler::BusConnect();
  134. LUAWatchesRequestMessages::Handler::BusConnect();
  135. LUATargetContextRequestMessages::Handler::BusConnect();
  136. HighlightedWords::Handler::BusConnect();
  137. AzFramework::AssetSystemInfoBus::Handler::BusConnect();
  138. // Connect to source control
  139. using SCConnectionBus = AzToolsFramework::SourceControlConnectionRequestBus;
  140. SCConnectionBus::Broadcast(&SCConnectionBus::Events::EnableSourceControl, true);
  141. //AzToolsFramework::RegisterAssetType(AzToolsFramework::RegisteredAssetType(ContextID, AZ::ScriptAsset::StaticAssetType(), "Script", ".lua", true, 0));
  142. m_pBreakpointSavedState = AZ::UserSettings::CreateFind<BreakpointSavedState>(AZ_CRC_CE("BreakpointSavedState"), AZ::UserSettings::CT_LOCAL);
  143. AzToolsFramework::MainWindowDescription desc;
  144. desc.name = "LUA Editor";
  145. desc.ContextID = ContextID;
  146. desc.hotkeyDesc = AzToolsFramework::HotkeyDescription(AZ_CRC_CE("LUAOpenEditor"), "Ctrl+Shift+L", "Open LUA Editor", "General", 1, AzToolsFramework::HotkeyDescription::SCOPE_WINDOW);
  147. AzToolsFramework::FrameworkMessages::Bus::Broadcast(&AzToolsFramework::FrameworkMessages::Bus::Events::AddComponentInfo, desc);
  148. LegacyFramework::IPCCommandBus::BroadcastResult(
  149. m_ipcOpenFilesHandle,
  150. &LegacyFramework::IPCCommandBus::Events::RegisterIPCHandler,
  151. "open_files",
  152. AZStd::bind(&Context::OnIPCOpenFiles, this, AZStd::placeholders::_1));
  153. bool connectedToAssetProcessor = false;
  154. // When the AssetProcessor is already launched it should take less than a second to perform a connection
  155. // but when the AssetProcessor needs to be launch it could take up to 15 seconds to have the AssetProcessor initialize
  156. // and able to negotiate a connection when running a debug build
  157. // and to negotiate a connection
  158. AzFramework::AssetSystem::ConnectionSettings connectionSettings;
  159. AzFramework::AssetSystem::ReadConnectionSettingsFromSettingsRegistry(connectionSettings);
  160. connectionSettings.m_connectionDirection = AzFramework::AssetSystem::ConnectionSettings::ConnectionDirection::ConnectToAssetProcessor;
  161. connectionSettings.m_connectionIdentifier = desc.name;
  162. AzFramework::AssetSystemRequestBus::BroadcastResult(connectedToAssetProcessor,
  163. &AzFramework::AssetSystemRequestBus::Events::EstablishAssetProcessorConnection, connectionSettings);
  164. if (!connectedToAssetProcessor)
  165. {
  166. AZ_TracePrintf(desc.name.c_str(), "%s was not able to connect to the Asset Processor. Please ensure that the Asset Processor is running.", desc.name.c_str());
  167. }
  168. }
  169. void Context::Deactivate()
  170. {
  171. LegacyFramework::IPCCommandBus::Broadcast(&LegacyFramework::IPCCommandBus::Events::UnregisterIPCHandler, m_ipcOpenFilesHandle);
  172. //AzToolsFramework::UnRegisterAssetType(AZ::ScriptAsset::StaticAssetType());
  173. LUATargetContextRequestMessages::Handler::BusDisconnect();
  174. LUAWatchesRequestMessages::Handler::BusDisconnect();
  175. LegacyFramework::CoreMessageBus::Handler::BusDisconnect();
  176. //AzToolsFramework::AssetManagementMessages::Handler::BusDisconnect(ContextID);
  177. ContextInterface::Handler::BusDisconnect(ContextID);
  178. Context_DocumentManagement::Handler::BusDisconnect();
  179. Context_DebuggerManagement::Handler::BusDisconnect();
  180. LUAStackRequestMessages::Handler::BusDisconnect();
  181. LUABreakpointRequestMessages::Handler::BusDisconnect();
  182. HighlightedWords::Handler::BusDisconnect();
  183. AzFramework::AssetSystemInfoBus::Handler::BusDisconnect();
  184. }
  185. void Context::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
  186. {
  187. required.push_back(AZ_CRC_CE("AssetProcessorToolsConnection"));
  188. }
  189. void Context::ApplicationDeactivated()
  190. {
  191. }
  192. void Context::ApplicationActivated()
  193. {
  194. if (m_bShuttingDown)
  195. {
  196. return;
  197. }
  198. if (m_bProcessingActivate)
  199. {
  200. return;
  201. }
  202. RefreshAllDocumentPerforceStat();
  203. // Open any files we specified in the command line.
  204. if (!m_filesToOpen.empty())
  205. {
  206. for (const auto& file : m_filesToOpen)
  207. {
  208. OpenAssetByPhysicalPath(file.c_str());
  209. }
  210. m_filesToOpen.clear();
  211. }
  212. if (m_pLUAEditorMainWindow)
  213. {
  214. m_pLUAEditorMainWindow->SetupLuaFilesPanel();
  215. }
  216. }
  217. bool Context::OnIPCOpenFiles(const AZStd::string& parameters)
  218. {
  219. if (parameters.empty())
  220. {
  221. return false;
  222. }
  223. AZStd::vector<AZStd::string> files;
  224. AZ::StringFunc::Tokenize(parameters.c_str(), files, ";");
  225. if (!files.empty())
  226. {
  227. for (const auto& file : files)
  228. {
  229. OpenAssetByPhysicalPath(file.c_str());
  230. }
  231. if (m_pLUAEditorMainWindow)
  232. {
  233. if (m_pLUAEditorMainWindow->isMinimized())
  234. {
  235. m_pLUAEditorMainWindow->showNormal();
  236. }
  237. else
  238. {
  239. m_pLUAEditorMainWindow->show();
  240. }
  241. m_pLUAEditorMainWindow->raise();
  242. m_pLUAEditorMainWindow->activateWindow();
  243. m_pLUAEditorMainWindow->setFocus();
  244. LUABreakpointTrackerMessages::Bus::Broadcast(
  245. &LUABreakpointTrackerMessages::Bus::Events::BreakpointsUpdate, m_pBreakpointSavedState->m_Breakpoints);
  246. }
  247. return true;
  248. }
  249. return false;
  250. }
  251. void Context::ApplicationShow(AZ::Uuid id)
  252. {
  253. if (ContextID == id)
  254. {
  255. ProvisionalShowAndFocus(true);
  256. }
  257. }
  258. void Context::ApplicationHide(AZ::Uuid id)
  259. {
  260. if (ContextID == id)
  261. {
  262. bool allowed = OnGetPermissionToShutDown(); // everyone must agree to hide before we can
  263. if (allowed)
  264. {
  265. m_pLUAEditorMainWindow->hide();
  266. auto newState = AZ::UserSettings::CreateFind<LUAEditorContextSavedState>(AZ_CRC_CE("LUA EDITOR CONTEXT STATE"), AZ::UserSettings::CT_LOCAL);
  267. newState->m_MainEditorWindowIsVisible = false;
  268. }
  269. }
  270. }
  271. void Context::ApplicationCensus()
  272. {
  273. auto newState = AZ::UserSettings::CreateFind<LUAEditorContextSavedState>(AZ_CRC_CE("LUA EDITOR CONTEXT STATE"), AZ::UserSettings::CT_LOCAL);
  274. AzToolsFramework::FrameworkMessages::Bus::Broadcast(
  275. &AzToolsFramework::FrameworkMessages::Bus::Events::ApplicationCensusReply, newState->m_MainEditorWindowIsVisible);
  276. }
  277. void Context::AddDefaultLUAKeywords()
  278. {
  279. AZStd::string keywords[] = {
  280. {"and"}, {"false"}, {"local"}, {"then"}, {"break"}, {"for"}, {"nil"}, {"true"}, {"do"},
  281. {"function"}, {"not"}, {"until"}, {"else"}, {"goto"}, {"or"}, {"while"}, {"elseif"}, {"if"}, {"repeat"},
  282. {"end"}, {"in"}, {"return"}
  283. };
  284. m_LUAKeywords.insert(std::begin(keywords), std::end(keywords));
  285. }
  286. void Context::AddDefaultLUALibraryFunctions()
  287. {
  288. AZStd::string librarys[] = {
  289. {"assert"}, {"collectgarbage"}, {"next"}, {"pairs"}, {"pcall"}, {"rawequal"}, {"rawget"}, {"rawlen"}, {"rawset"},
  290. {"select"}, {"setmetatable"}, {"tonumber"}, {"tostring"}, {"type"}, {"_VERSION"}, {"xpcall"}, {"coroutine.create"}, {"coroutine.resume"}, {"coroutine.running"},
  291. {"coroutine.status"}, {"coroutine.wrap"}, {"coroutine.yield"}, {"string.byte"}, {"string.char"}, {"string.dump"}, {"string.find"}, {"string.format"},
  292. {"string.gmatch"}, {"string.gsub"}, {"string.len"}, {"string.lower"}, {"string.match"}, {"string.rep"}, {"string.reverse"}, {"string.sub"}, {"string.upper"},
  293. {"table.concat"}, {"table.insert"}, {"table.pack"}, {"table.remove"}, {"table.sort"}, {"table.unpack"}, {"math.abs"}, {"math.acos"}, {"math.asin"},
  294. {"math.atan"}, {"math.atan2"}, {"math.ceil"}, {"math.cos"}, {"math.cosh"}, {"math.deg"}, {"math.exp"}, {"math.floor"}, {"math.fmod"}, {"math.frexp"},
  295. {"math.huge"}, {"math.ldexp"}, {"math.log"}, {"math.max"}, {"math.min"}, {"math.modf"}, {"math.pi"}, {"math.pow"}, {"math.rad"}, {"math.random"}, {"math.randomseed"},
  296. {"math.sin"}, {"math.sinh"}, {"math.sqrt"}, {"math.tan"}, {"math.tanh"}, {"bit32.arshift"}, {"bit32.band"}, {"bit32.bnot"}, {"bit32.bor"}, {"bit32.btest"},
  297. {"bit32.bxor"}, {"bit32.extract"}, {"bit32.replace"}, {"bit32.lrotate"}, {"bit32.lshift"}, {"bit32.rrotate"}, {"bit32.rshift"}
  298. };
  299. m_LUALibraryFunctions.insert(std::begin(librarys), std::end(librarys));
  300. }
  301. void Context::RefreshAllDocumentPerforceStat()
  302. {
  303. m_bProcessingActivate = true;
  304. AZ_TracePrintf(LUAEditorDebugName, "Entry refreshalldocumentperforceStat()\n");
  305. for (DocumentInfoMap::iterator it = m_documentInfoMap.begin(); it != m_documentInfoMap.end(); ++it)
  306. {
  307. DocumentInfo& info = it->second;
  308. {
  309. AZ_TracePrintf(LUAEditorDebugName, "Refreshing Perforce for assetId '%s' '%s'\n", info.m_assetId.c_str(), info.m_assetName.c_str());
  310. }
  311. if (m_fileIO && !info.m_bUntitledDocument)
  312. {
  313. // check for updated modtime:
  314. if (!m_fileIO->Exists(info.m_assetId.c_str()))
  315. {
  316. AZ_TracePrintf(LUAEditorDebugName, "During Refresh, a document appears to have been removed from disk: \"%s\"\n", info.m_assetName.c_str());
  317. // this can happen if they mark something for delete...
  318. info.m_bIsModified = true;
  319. info.m_bSourceControl_CanWrite = true;
  320. if (m_pLUAEditorMainWindow)
  321. {
  322. m_pLUAEditorMainWindow->OnDocumentInfoUpdated(info);
  323. }
  324. }
  325. else
  326. {
  327. uint64_t lastKnownModTime = (AZ::u64)info.m_lastKnownModTime.dwHighDateTime << 32 | info.m_lastKnownModTime.dwLowDateTime;
  328. uint64_t modTime = m_fileIO->ModificationTime(info.m_assetId.c_str());
  329. if (lastKnownModTime != modTime)
  330. {
  331. // ruh oh! The file time of the asset changed - someone reverted, modified, etc. What do we do?
  332. // do we have unsaved changes?
  333. info.m_lastKnownModTime.dwHighDateTime = static_cast<DWORD>(modTime >> 32);
  334. info.m_lastKnownModTime.dwLowDateTime = static_cast<DWORD>(modTime);
  335. {
  336. AZ_TracePrintf(LUAEditorDebugName, "Document modtime has changed, queueing reload of '%s' '%s'\n", info.m_assetId.c_str(), info.m_assetName.c_str());
  337. }
  338. // async crossover to test files being written against asset changes
  339. if (info.m_bDataIsWritten)
  340. {
  341. m_reloadCheckDocuments.insert(info.m_assetId);
  342. if (!m_bReloadCheckQueued)
  343. {
  344. m_bReloadCheckQueued = true;
  345. AZ::SystemTickBus::QueueFunction(&Context::ProcessReloadCheck, this);
  346. }
  347. }
  348. }
  349. if (!info.m_bSourceControl_BusyGettingStats)
  350. {
  351. // it's OK to skip getting fresh file info from Perforce here
  352. // because we've already given the go-ahead to exit the Application
  353. if (!m_bShuttingDown)
  354. {
  355. {
  356. AZ_TracePrintf(LUAEditorDebugName, "Queuing P4 Refresh of '%s' '%s'\n", info.m_assetId.c_str(), info.m_assetName.c_str());
  357. }
  358. info.m_bSourceControl_BusyGettingStats = true;
  359. // while we're reading it, fetch the perforce information for it:
  360. //AZ_TracePrintf("Debug"," ++m_numOutstandingOperations %d\n",__LINE__);
  361. ++m_numOutstandingOperations;
  362. using SCCommandBus = AzToolsFramework::SourceControlCommandBus;
  363. SCCommandBus::Broadcast(&SCCommandBus::Events::GetFileInfo,
  364. info.m_assetId.c_str(),
  365. AZStd::bind(&Context::PerforceStatResponseCallback,
  366. this,
  367. AZStd::placeholders::_1,
  368. AZStd::placeholders::_2,
  369. info.m_assetId)
  370. );
  371. // check for updated modtime, too...
  372. }
  373. }
  374. }
  375. }
  376. }
  377. m_bProcessingActivate = false;
  378. AZ_TracePrintf(LUAEditorDebugName, "Finished refreshalldocumentperforceStat()\n");
  379. }
  380. void Context::ProcessReloadCheck()
  381. {
  382. AZ_TracePrintf(LUAEditorDebugName, "ProcessReloadCheck ProcessReloadCheck()\n");
  383. m_bReloadCheckQueued = false;
  384. for (auto currentDoc = m_reloadCheckDocuments.begin(); currentDoc != m_reloadCheckDocuments.end(); ++currentDoc)
  385. {
  386. DocumentInfoMap::iterator docInfoIter = m_documentInfoMap.find(*currentDoc);
  387. if (docInfoIter == m_documentInfoMap.end())
  388. {
  389. continue;
  390. }
  391. DocumentInfo& info = docInfoIter->second;
  392. {
  393. AZ_TracePrintf(LUAEditorDebugName, "ProcessReloadCheck inspecting assetId '%s' '%s'\n", info.m_assetId.c_str(), info.m_assetName.c_str());
  394. }
  395. auto newState = AZ::UserSettings::CreateFind<LUAEditorMainWindowSavedState>(AZ_CRC_CE("LUA EDITOR MAIN WINDOW STATE"), AZ::UserSettings::CT_LOCAL);
  396. // Check to see if it is unmodified and the setting is set to auto-reload unmodified files
  397. bool shouldAutoReload = newState->m_bAutoReloadUnmodifiedFiles && !info.m_bIsModified;
  398. bool shouldReload = false;
  399. if (!shouldAutoReload)
  400. {
  401. // we may have unsaved changes:
  402. QMessageBox msgBox(this->m_pLUAEditorMainWindow);
  403. msgBox.setText("A file has been modified by an outside program. Would you like to reload it from disk? If you do, you will lose any unsaved changes.");
  404. msgBox.setInformativeText(info.m_assetName.c_str());
  405. msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
  406. msgBox.setButtonText(QMessageBox::Yes, "Reload From Disk");
  407. msgBox.setButtonText(QMessageBox::No, "Don't reload");
  408. msgBox.setDefaultButton(QMessageBox::No);
  409. msgBox.setIcon(QMessageBox::Question);
  410. shouldReload = (msgBox.exec() == QMessageBox::Yes);
  411. }
  412. if (shouldAutoReload || shouldReload)
  413. {
  414. // queue document reopen!
  415. {
  416. AZ_TracePrintf(LUAEditorDebugName, "ProcessReloadCheck user queueing reload for assetId '%s' '%s'\n", info.m_assetId.c_str(), info.m_assetName.c_str());
  417. }
  418. AZ::SystemTickBus::QueueFunction(&Context::OnReloadDocument, this, info.m_assetId);
  419. }
  420. else
  421. {
  422. // document remains open and modified, we don't overwrite or reload!
  423. // but also don't prompt them -update the modtime.
  424. if (!info.m_assetId.empty())
  425. {
  426. if (m_fileIO)
  427. {
  428. uint64_t modTime = m_fileIO->ModificationTime(info.m_assetId.c_str());
  429. info.m_lastKnownModTime.dwHighDateTime = static_cast<DWORD>(modTime >> 32);
  430. info.m_lastKnownModTime.dwLowDateTime = static_cast<DWORD>(modTime);
  431. }
  432. }
  433. }
  434. }
  435. AZ_TracePrintf(LUAEditorDebugName, "Exit ProcessReloadCheck()\n");
  436. }
  437. void Context::DesiredTargetConnected(bool connected)
  438. {
  439. AZ_TracePrintf(LUAEditorDebugName, "Context::DesiredTargetConnected( %d )\n", connected);
  440. if (connected)
  441. {
  442. LUAEditorDebuggerMessages::Bus::Broadcast(&LUAEditorDebuggerMessages::Bus::Events::EnumerateContexts);
  443. LUAEditorMainWindowMessages::Bus::Broadcast(&LUAEditorMainWindowMessages::Bus::Events::OnConnectedToTarget);
  444. LUAEditor::Context_ControlManagement::Bus::Broadcast(&LUAEditor::Context_ControlManagement::Bus::Events::OnTargetConnected);
  445. m_connectedState = true;
  446. }
  447. else
  448. {
  449. LUAEditorMainWindowMessages::Bus::Broadcast(&LUAEditorMainWindowMessages::Bus::Events::OnDisconnectedFromTarget);
  450. LUAEditor::Context_ControlManagement::Bus::Broadcast(&LUAEditor::Context_ControlManagement::Bus::Events::OnTargetDisconnected);
  451. LUAEditor::Context_DebuggerManagement::Bus::Broadcast(&LUAEditor::Context_DebuggerManagement::Bus::Events::OnDebuggerDetached);
  452. m_connectedState = false;
  453. }
  454. }
  455. void Context::DesiredTargetChanged([[maybe_unused]] AZ::u32 newTargetID, AZ::u32 oldTargetID)
  456. {
  457. AZ_TracePrintf(LUAEditorDebugName, "Context::RemoteTargetChanged()\n");
  458. // If there's no prior target, there's nothing to detach
  459. if (oldTargetID != 0)
  460. {
  461. RequestDetachDebugger();
  462. }
  463. }
  464. //////////////////////////////////////////////////////////////////////////
  465. //BreakpointTracker Request Messages
  466. const BreakpointMap* Context::RequestBreakpoints()
  467. {
  468. return &m_pBreakpointSavedState->m_Breakpoints;
  469. }
  470. //////////////////////////////////////////////////////////////////////////
  471. //StackTracker Request Messages
  472. void Context::RequestStackClicked(const AZStd::string& stackString, int lineNumber)
  473. {
  474. // incoming display string looks like this:
  475. /*
  476. "[Lua] gameinfo\script\player\playercharacter_strider (587) : PreStateTick(37BA18D8, 0.033333)"
  477. */
  478. // outgoing string for the asset name should look like this:
  479. /*
  480. "gameinfo\script\player\playercharacter_strider"
  481. */
  482. QString script = stackString.c_str();
  483. script = script.right(script.length() - 6);
  484. script = script.left(script.indexOf(" "));
  485. RequestEditorFocus(script.toUtf8().data(), lineNumber);
  486. }
  487. //////////////////////////////////////////////////////////////////////////
  488. //TargetContextTracker Request Messages
  489. const AZStd::vector<AZStd::string> Context::RequestTargetContexts()
  490. {
  491. return m_targetContexts;
  492. }
  493. const AZStd::string Context::RequestCurrentTargetContext()
  494. {
  495. return m_currentTargetContext;
  496. }
  497. void Context::RequestEditorFocus(const AZStd::string& relativePath, int lineNumber)
  498. {
  499. AZStd::to_lower(const_cast<AZStd::string&>(relativePath).begin(), const_cast<AZStd::string&>(relativePath).end());
  500. bool foundAbsolutePath = false;
  501. AZStd::string absolutePath;
  502. AzToolsFramework::AssetSystemRequestBus::BroadcastResult(foundAbsolutePath,
  503. &AzToolsFramework::AssetSystemRequestBus::Events::GetFullSourcePathFromRelativeProductPath, relativePath, absolutePath);
  504. bool fileFound = false;
  505. if (foundAbsolutePath)
  506. {
  507. AZStd::to_lower(const_cast<AZStd::string&>(absolutePath).begin(), const_cast<AZStd::string&>(absolutePath).end());
  508. auto docInfoIter = m_documentInfoMap.find(absolutePath);
  509. fileFound = docInfoIter != m_documentInfoMap.end();
  510. }
  511. if (!fileFound)
  512. {
  513. // Check if we have a relative path, we may still be able to open the file (this may happen when double clicking on a stack panel entry)
  514. for (const auto& doc : m_documentInfoMap)
  515. {
  516. const char* result = strstr(doc.first.c_str(), relativePath.c_str());
  517. if (result)
  518. {
  519. absolutePath = doc.second.m_assetId;
  520. fileFound = true;
  521. break;
  522. }
  523. }
  524. if (!fileFound)
  525. {
  526. // the document was probably closed.
  527. if (foundAbsolutePath)
  528. {
  529. AssetOpenRequested(absolutePath, true);
  530. }
  531. else
  532. {
  533. AssetOpenRequested(relativePath, true);
  534. }
  535. return;
  536. }
  537. }
  538. ProvisionalShowAndFocus();
  539. // tell the view that it needs to focus that document!
  540. m_pLUAEditorMainWindow->OnRequestFocusView(absolutePath);
  541. m_pLUAEditorMainWindow->MoveEditCursor(absolutePath, lineNumber, true);
  542. }
  543. void Context::RequestDeleteBreakpoint(const AZStd::string& assetIdString, int lineNumber)
  544. {
  545. for (BreakpointMap::iterator it = m_pBreakpointSavedState->m_Breakpoints.begin(); it != m_pBreakpointSavedState->m_Breakpoints.end(); ++it)
  546. {
  547. const Breakpoint& bp = it->second;
  548. if (bp.m_assetName == assetIdString)
  549. {
  550. if (bp.m_documentLine == lineNumber - 1) // -1 offset is counter to the +1 line numbering scheme used by the LUA editor
  551. {
  552. DeleteBreakpoint(bp.m_breakpointId);
  553. LUABreakpointTrackerMessages::Bus::Broadcast(
  554. &LUABreakpointTrackerMessages::Bus::Events::BreakpointsUpdate, m_pBreakpointSavedState->m_Breakpoints);
  555. break;
  556. }
  557. }
  558. }
  559. }
  560. //////////////////////////////////////////////////////////////////////////
  561. // AzToolsFramework CoreMessages and support
  562. void Context::OnRestoreState()
  563. {
  564. const AZStd::string k_launchString = "launch";
  565. const AZStd::string k_luaEditorString = "lua";
  566. const AZStd::string k_luaScriptFileString = "files";
  567. // the world editor considers itself a default window, so it always makes one
  568. typedef AzToolsFramework::FrameworkMessages::Bus HotkeyBus;
  569. // register our hotkeys so that they exist in the preferences panel even if we're not open:
  570. HotkeyBus::Broadcast(&HotkeyBus::Events::RegisterHotkey, AzToolsFramework::HotkeyDescription(AZ_CRC_CE("LUAFind"), "Ctrl+F", "Find", "LUA Editor", 1, AzToolsFramework::HotkeyDescription::SCOPE_WINDOW));
  571. HotkeyBus::Broadcast(&HotkeyBus::Events::RegisterHotkey, AzToolsFramework::HotkeyDescription(AZ_CRC_CE("LUAQuickFindLocal"), "Ctrl+F3", "Quick Find Local", "LUA Editor", 1, AzToolsFramework::HotkeyDescription::SCOPE_WINDOW));
  572. HotkeyBus::Broadcast(&HotkeyBus::Events::RegisterHotkey, AzToolsFramework::HotkeyDescription(AZ_CRC_CE("LUAQuickFindLocalReverse"), "Ctrl+Shift+F3", "Quick Find Local (Reverse)", "LUA Editor", 1, AzToolsFramework::HotkeyDescription::SCOPE_WINDOW));
  573. HotkeyBus::Broadcast(&HotkeyBus::Events::RegisterHotkey, AzToolsFramework::HotkeyDescription(AZ_CRC_CE("LUAFindInFiles"), "Ctrl+Shift+F", "Find In Files", "LUA Editor", 1, AzToolsFramework::HotkeyDescription::SCOPE_WINDOW));
  574. HotkeyBus::Broadcast(&HotkeyBus::Events::RegisterHotkey, AzToolsFramework::HotkeyDescription(AZ_CRC_CE("LUAReplace"), "Ctrl+R", "Replace", "LUA Editor", 1, AzToolsFramework::HotkeyDescription::SCOPE_WINDOW));
  575. HotkeyBus::Broadcast(&HotkeyBus::Events::RegisterHotkey, AzToolsFramework::HotkeyDescription(AZ_CRC_CE("LUAReplaceInFiles"), "Ctrl+Shift+R", "Replace In Files", "LUA Editor", 1, AzToolsFramework::HotkeyDescription::SCOPE_WINDOW));
  576. HotkeyBus::Broadcast(&HotkeyBus::Events::RegisterHotkey, AzToolsFramework::HotkeyDescription(AZ_CRC_CE("LUAGoToLine"), "Ctrl+G", "Go to line number...", "LUA Editor", 1, AzToolsFramework::HotkeyDescription::SCOPE_WINDOW));
  577. HotkeyBus::Broadcast(&HotkeyBus::Events::RegisterHotkey, AzToolsFramework::HotkeyDescription(AZ_CRC_CE("LUAFold"), "Alt+0", "Fold Source Functions", "LUA Editor", 1, AzToolsFramework::HotkeyDescription::SCOPE_WINDOW));
  578. HotkeyBus::Broadcast(&HotkeyBus::Events::RegisterHotkey, AzToolsFramework::HotkeyDescription(AZ_CRC_CE("LUAUnfold"), "Alt+Shift+0", "Unfold Source Functions", "LUA Editor", 1, AzToolsFramework::HotkeyDescription::SCOPE_WINDOW));
  579. HotkeyBus::Broadcast(&HotkeyBus::Events::RegisterHotkey, AzToolsFramework::HotkeyDescription(AZ_CRC_CE("LUACloseAllExceptCurrent"), "Ctrl+Alt+F4", "Close All Windows Except Current", "LUA Editor", 1, AzToolsFramework::HotkeyDescription::SCOPE_WINDOW));
  580. HotkeyBus::Broadcast(&HotkeyBus::Events::RegisterHotkey, AzToolsFramework::HotkeyDescription(AZ_CRC_CE("LUACloseAll"), "Ctrl+Shift+F4", "Close All Windows", "LUA Editor", 1, AzToolsFramework::HotkeyDescription::SCOPE_WINDOW));
  581. HotkeyBus::Broadcast(&HotkeyBus::Events::RegisterHotkey, AzToolsFramework::HotkeyDescription(AZ_CRC_CE("LUAComment"), "Ctrl+K", "Comment Selected Block", "LUA Editor", 1, AzToolsFramework::HotkeyDescription::SCOPE_WINDOW));
  582. HotkeyBus::Broadcast(&HotkeyBus::Events::RegisterHotkey, AzToolsFramework::HotkeyDescription(AZ_CRC_CE("LUAUncomment"), "Ctrl+Shift+K", "Uncomment Selected Block", "LUA Editor", 1, AzToolsFramework::HotkeyDescription::SCOPE_WINDOW));
  583. HotkeyBus::Broadcast(&HotkeyBus::Events::RegisterHotkey, AzToolsFramework::HotkeyDescription(AZ_CRC_CE("LUALinesUpTranspose"), "Ctrl+Shift+Up", "Transpose Lines Up", "LUA Editor", 1, AzToolsFramework::HotkeyDescription::SCOPE_WINDOW));
  584. HotkeyBus::Broadcast(&HotkeyBus::Events::RegisterHotkey, AzToolsFramework::HotkeyDescription(AZ_CRC_CE("LUALinesDnTranspose"), "Ctrl+Shift+Down", "Transpose Lines Down", "LUA Editor", 1, AzToolsFramework::HotkeyDescription::SCOPE_WINDOW));
  585. HotkeyBus::Broadcast(&HotkeyBus::Events::RegisterHotkey, AzToolsFramework::HotkeyDescription(AZ_CRC_CE("LUAResetZoom"), "Ctrl+0", "Reset Default Zoom", "LUA Editor", 1, AzToolsFramework::HotkeyDescription::SCOPE_WINDOW));
  586. bool GUIMode = true;
  587. LegacyFramework::FrameworkApplicationMessages::Bus::BroadcastResult(
  588. GUIMode, &LegacyFramework::FrameworkApplicationMessages::Bus::Events::IsRunningInGUIMode);
  589. if (!GUIMode)
  590. {
  591. // do not auto create lua editor main window in batch mode.
  592. return;
  593. }
  594. const AZ::CommandLine* commandLine = nullptr;
  595. AZ::ComponentApplicationBus::Broadcast([&commandLine](AZ::ComponentApplicationRequests* requests)
  596. {
  597. commandLine = requests->GetAzCommandLine();
  598. });
  599. bool forceShow = false;
  600. bool forceHide = false;
  601. if (commandLine->HasSwitch(k_launchString))
  602. {
  603. forceHide = true;
  604. size_t numSwitchValues = commandLine->GetNumSwitchValues(k_launchString);
  605. for (size_t i = 0; i < numSwitchValues; ++i)
  606. {
  607. AZStd::string inputValue = commandLine->GetSwitchValue(k_launchString, i);
  608. if (inputValue.compare(k_luaEditorString) == 0)
  609. {
  610. forceShow = true;
  611. forceHide = false;
  612. }
  613. }
  614. }
  615. size_t numSwitchValues = commandLine->GetNumSwitchValues(k_luaScriptFileString);
  616. if (numSwitchValues >= 1)
  617. {
  618. m_filesToOpen.clear();
  619. for (size_t i = 0; i < numSwitchValues; ++i)
  620. {
  621. AZStd::string inputValue = commandLine->GetSwitchValue(k_luaScriptFileString, i);
  622. // Cache the files we want to open, we will open them when we activate the main window.
  623. m_filesToOpen.push_back(inputValue);
  624. }
  625. }
  626. ProvisionalShowAndFocus(forceShow, forceHide);
  627. }
  628. bool Context::OnGetPermissionToShutDown() // until everyone returns true, we can't shut down.
  629. {
  630. AZ_TracePrintf(LUAEditorDebugName, "Context::OnGetPermissionToShutDown()\n");
  631. if (m_pLUAEditorMainWindow)
  632. {
  633. if (!m_pLUAEditorMainWindow->OnGetPermissionToShutDown())
  634. {
  635. return false;
  636. }
  637. }
  638. m_bShuttingDown = true;
  639. return true;
  640. }
  641. // until everyone returns true, we can't shut down.
  642. bool Context::CheckOkayToShutDown()
  643. {
  644. if (m_pLUAEditorMainWindow)
  645. {
  646. // confirmation that we're quitting.
  647. if (m_pLUAEditorMainWindow->isVisible())
  648. {
  649. m_pLUAEditorMainWindow->setEnabled(false);
  650. m_pLUAEditorMainWindow->hide();
  651. }
  652. }
  653. if (m_numOutstandingOperations > 0)
  654. {
  655. AZ_TracePrintf(LUAEditorDebugName, "CheckOkayToShutDown() return FALSE with (%d) OutstandingOperations\n", static_cast<int>(m_numOutstandingOperations));
  656. return false;
  657. }
  658. return true;
  659. }
  660. void Context::OnSaveState()
  661. {
  662. // notify main view to persist?
  663. if (m_pLUAEditorMainWindow)
  664. {
  665. m_pLUAEditorMainWindow->SaveWindowState();
  666. }
  667. }
  668. // Script interface:
  669. void Context::LoadLayout()
  670. {
  671. if (m_pLUAEditorMainWindow)
  672. {
  673. m_pLUAEditorMainWindow->RestoreWindowState();
  674. }
  675. }
  676. void Context::SaveLayout()
  677. {
  678. if (m_pLUAEditorMainWindow)
  679. {
  680. m_pLUAEditorMainWindow->SaveWindowState();
  681. }
  682. }
  683. void Context::OnDestroyState()
  684. {
  685. m_documentInfoMap.clear();
  686. if (m_pLUAEditorMainWindow)
  687. {
  688. delete m_pLUAEditorMainWindow;
  689. }
  690. m_pLUAEditorMainWindow = nullptr;
  691. }
  692. //////////////////////////////////////////////////////////////////////////
  693. //////////////////////////////////////////////////////////////////////////
  694. //ContextInterface
  695. //////////////////////////////////////////////////////////////////////////
  696. // Utility
  697. void Context::ProvisionalShowAndFocus(bool forcedShow, bool forcedHide)
  698. {
  699. // main view will auto persist (load)
  700. auto newState = AZ::UserSettings::CreateFind<LUAEditorContextSavedState>(AZ_CRC_CE("LUA EDITOR CONTEXT STATE"), AZ::UserSettings::CT_LOCAL);
  701. if (forcedShow)
  702. {
  703. newState->m_MainEditorWindowIsOpen = true;
  704. newState->m_MainEditorWindowIsVisible = true;
  705. }
  706. else if (forcedHide)
  707. {
  708. newState->m_MainEditorWindowIsOpen = false;
  709. newState->m_MainEditorWindowIsVisible = false;
  710. }
  711. if (newState->m_MainEditorWindowIsOpen)
  712. {
  713. if (newState->m_MainEditorWindowIsVisible)
  714. {
  715. if (!m_pLUAEditorMainWindow)
  716. {
  717. m_pLUAEditorMainWindow = aznew LUAEditorMainWindow(m_referenceModel, m_connectedState);
  718. }
  719. m_pLUAEditorMainWindow->show();
  720. m_pLUAEditorMainWindow->raise();
  721. m_pLUAEditorMainWindow->activateWindow();
  722. m_pLUAEditorMainWindow->setFocus();
  723. }
  724. else
  725. {
  726. if (m_pLUAEditorMainWindow)
  727. {
  728. m_pLUAEditorMainWindow->hide();
  729. }
  730. }
  731. LUABreakpointTrackerMessages::Bus::Broadcast(
  732. &LUABreakpointTrackerMessages::Bus::Events::BreakpointsUpdate, m_pBreakpointSavedState->m_Breakpoints);
  733. }
  734. }
  735. //////////////////////////////////////////////////////////////////////////
  736. // Context_DocumentManagement Messages
  737. void Context::OnNewDocument(const AZStd::string& assetId)
  738. {
  739. ShowLUAEditorView();
  740. AZStd::string normalizedAssetId = assetId;
  741. AZStd::to_lower(normalizedAssetId.begin(), normalizedAssetId.end());
  742. // make sure we have a name that is not already tracked
  743. DocumentInfoMap::pair_iter_bool infoEntry = m_documentInfoMap.insert_key(normalizedAssetId);
  744. DocumentInfo& info = infoEntry.first->second;
  745. info.m_assetId = normalizedAssetId;
  746. info.m_assetName = assetId;
  747. AZ::StringFunc::Path::GetFullFileName(assetId.c_str(), info.m_displayName);
  748. info.m_bSourceControl_Ready = true;
  749. info.m_bSourceControl_CanWrite = true;
  750. info.m_bUntitledDocument = false;
  751. info.m_bIsBeingSaved = false;
  752. info.m_scriptAsset = "";
  753. // now open a view that will end up with its info (the view will have a progress bar on it as it loads)
  754. m_pLUAEditorMainWindow->OnOpenLUAView(info);
  755. // now since there is no actual loading we just tell its done, since the document is untitled it wont try
  756. // retrieve the document data in the call
  757. info.m_bDataIsLoaded = true;
  758. m_pLUAEditorMainWindow->OnDocumentInfoUpdated(info);
  759. }
  760. void Context::OnLoadDocument(const AZStd::string& assetId, bool errorOnNotFound)
  761. {
  762. AssetOpenRequested(assetId, errorOnNotFound);
  763. }
  764. void Context::OnCloseDocument(const AZStd::string& id)
  765. {
  766. AZStd::string assetId = id; // as we might delete the reference
  767. if (m_pLUAEditorMainWindow)
  768. {
  769. m_pLUAEditorMainWindow->OnCloseView(assetId);
  770. }
  771. auto documentInfoIter = FindDocumentInfo(assetId);
  772. if (documentInfoIter.has_value())
  773. {
  774. m_documentInfoMap.erase(documentInfoIter.value());
  775. }
  776. CleanUpBreakpoints();
  777. }
  778. void Context::OnSaveDocument(const AZStd::string& assetId, bool bCloseAfterSave, bool bSaveAs)
  779. {
  780. AZ_TracePrintf(LUAEditorDebugName, AZStd::string::format("LUAEditor OnSaveDocument" "%s\n", assetId.c_str()).c_str());
  781. if (!m_pLUAEditorMainWindow)
  782. {
  783. return;
  784. }
  785. // Make a copy because it may be modified behind our backs by later bus calls
  786. AZStd::string originalAssetId = assetId;
  787. auto documentInfoIter = FindDocumentInfo(assetId);
  788. if (!documentInfoIter.has_value())
  789. {
  790. AZ_TracePrintf(LUAEditorDebugName, "Context::OnSaveDocument - Document with ID is already closed - ignoring.\n");
  791. return;
  792. }
  793. DocumentInfo& documentInfo = documentInfoIter.value()->second;
  794. AZStd::string newAssetName(documentInfo.m_assetName);
  795. bool newFileCreated = false;
  796. if (documentInfo.m_bIsBeingSaved)
  797. {
  798. return;
  799. }
  800. bool trySaveAs = documentInfo.m_bUntitledDocument || bSaveAs;
  801. while (trySaveAs)
  802. {
  803. if (!m_pLUAEditorMainWindow->OnFileSaveDialog(documentInfo.m_assetName, newAssetName))
  804. {
  805. return;
  806. }
  807. // the file dialog lets us do silly things like choose the same name as the original
  808. // in which case we should treat it just like a regular save
  809. if (newAssetName == documentInfo.m_assetName)
  810. {
  811. documentInfo.m_bUntitledDocument = false;
  812. break;
  813. }
  814. // do not allow SaveAs onto an existing asset, even if it could be checked out and modified "safely."
  815. // end user must check out and modify contents directly if they want this
  816. if (AZ::StringFunc::Find(newAssetName.c_str(), ".lua") == AZStd::string::npos)
  817. {
  818. newAssetName += ".lua";
  819. }
  820. AZ::Data::AssetId catalogAssetId;
  821. AZ::Data::AssetCatalogRequestBus::BroadcastResult(
  822. catalogAssetId,
  823. &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath,
  824. newAssetName.c_str(),
  825. AZ::AzTypeInfo<AZ::ScriptAsset>::Uuid(),
  826. false);
  827. if (catalogAssetId.IsValid() || m_fileIO->Exists(newAssetName.c_str()))
  828. {
  829. QMessageBox::warning(m_pLUAEditorMainWindow, "Warning", "You Cannot SaveAs Over An Existing Asset\nPlease Check And Try A New Filename");
  830. continue;
  831. }
  832. trySaveAs = false;
  833. documentInfo.m_bUntitledDocument = false;
  834. AZ::StringFunc::Path::GetFullFileName(newAssetName.c_str(), documentInfo.m_displayName);
  835. // when you 'save as' you can write to it, even if it started out not that way.
  836. documentInfo.m_bSourceControl_Ready = true;
  837. documentInfo.m_bSourceControl_CanWrite = true;
  838. newFileCreated = true;
  839. }
  840. if (!documentInfo.m_bSourceControl_CanWrite)
  841. {
  842. AZ_Warning("LUA Editor Error", false, "<div severity=\"warning\">Unable to save document - the document is read-only.</div>");
  843. }
  844. documentInfo.m_bDataIsWritten = false;
  845. documentInfo.m_bCloseAfterSave = bCloseAfterSave;
  846. documentInfo.m_bIsBeingSaved = true;
  847. ++m_numOutstandingOperations;
  848. //////////////////////////////////////////////////////////////////////////
  849. // Blocking test for now (use the streamer later)
  850. // insert with the proper ID (saved file)
  851. bool isSaved = false;
  852. AZ::IO::SystemFile luaFile;
  853. if (luaFile.Open(newAssetName.c_str(), AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY))
  854. {
  855. luaFile.Write(documentInfo.m_scriptAsset.c_str(), documentInfo.m_scriptAsset.size());
  856. isSaved = true;
  857. luaFile.Close();
  858. }
  859. // Open the newly saved file
  860. if (isSaved)
  861. {
  862. if (newFileCreated)
  863. {
  864. documentInfo.m_bCloseAfterSave = true;
  865. AZStd::string normalizedAssetId = newAssetName;
  866. AZStd::to_lower(normalizedAssetId.begin(), normalizedAssetId.end());
  867. Context_DocumentManagement::Bus::Broadcast(
  868. &Context_DocumentManagement::Bus::Events::OnLoadDocument, normalizedAssetId, true);
  869. DocumentCheckOutRequested(normalizedAssetId);
  870. }
  871. }
  872. DataSaveDoneCallback(isSaved, originalAssetId);
  873. //////////////////////////////////////////////////////////////////////////
  874. m_pLUAEditorMainWindow->OnDocumentInfoUpdated(documentInfo);
  875. }
  876. bool Context::OnSaveDocumentAs(const AZStd::string& assetId, bool bCloseAfterSave)
  877. {
  878. AZ_TracePrintf(LUAEditorDebugName, AZStd::string::format("LUAEditor OnSaveDocumentAs" "%s\n", assetId.c_str()).c_str());
  879. [[maybe_unused]] auto documentInfoIter = FindDocumentInfo(assetId);
  880. AZ_Assert(documentInfoIter.has_value(), "LUAEditor OnSaveDocumentAs() : Cant find Document Info.");
  881. OnSaveDocument(assetId, bCloseAfterSave, true);
  882. return true;
  883. }
  884. void Context::DocumentCheckOutRequested(const AZStd::string& assetId)
  885. {
  886. auto documentInfoIter = FindDocumentInfo(assetId);
  887. AZ_Assert(documentInfoIter.has_value(), "Invalid document lookup.");
  888. AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
  889. AZ_Assert(fileIO, "FileIO system is not present.");
  890. if (fileIO && !fileIO->Exists(assetId.c_str()))
  891. {
  892. AZ_Warning(LUAEditorInfoName, false, "%s : Unable to check out file from source control, it may need to be saved first.", assetId.c_str());
  893. return;
  894. }
  895. DocumentInfo& documentInfo = documentInfoIter.value()->second;
  896. AZ_TracePrintf(LUAEditorDebugName, "LUAEditor DocumentCheckOutRequested: %s\n", documentInfo.m_assetName.c_str());
  897. documentInfo.m_bSourceControl_BusyRequestingEdit = true;
  898. AZStd::string sourceFile;
  899. bool fileFound = false;
  900. using AssetSysReqBus = AzToolsFramework::AssetSystemRequestBus;
  901. AssetSysReqBus::BroadcastResult(fileFound, &AssetSysReqBus::Events::GetFullSourcePathFromRelativeProductPath, assetId.c_str(), sourceFile);
  902. if (!fileFound)
  903. {
  904. // This warning can be tripped if the LuaIDE loses connection with the asset processor.
  905. AZ_Warning(LUAEditorInfoName, false, "The Lua IDE source control integration requires an active connection to the Asset Processor. Make sure Asset Processor is running.");
  906. // Reset BusyRequestingEdit or we'll be stuck with the "checking out" loading bar for forever
  907. documentInfo.m_bSourceControl_BusyRequestingEdit = false;
  908. return;
  909. }
  910. ////AZ_TracePrintf("Debug"," ++m_numOutstandingOperations %d\n",__LINE__);
  911. ++m_numOutstandingOperations;
  912. using SCCommandBus = AzToolsFramework::SourceControlCommandBus;
  913. SCCommandBus::Broadcast(&SCCommandBus::Events::RequestEdit,
  914. sourceFile.c_str(),
  915. true,
  916. AZStd::bind(&Context::PerforceRequestEditCallback,
  917. this,
  918. AZStd::placeholders::_1,
  919. AZStd::placeholders::_2,
  920. assetId)
  921. );
  922. }
  923. AZStd::optional<const Context::DocumentInfoMap::iterator> Context::FindDocumentInfo(const AZStd::string_view assetId)
  924. {
  925. AZStd::string assetIdLower(assetId);
  926. AZStd::to_lower(assetIdLower.begin(), assetIdLower.end());
  927. DocumentInfoMap::iterator documentIter = m_documentInfoMap.find(assetIdLower);
  928. if (documentIter == m_documentInfoMap.end())
  929. {
  930. return AZStd::nullopt;
  931. }
  932. return AZStd::optional<const Context::DocumentInfoMap::iterator>(documentIter);
  933. }
  934. //////////////////////////////////////////////////////////////////////////
  935. //AssetManagementMessages
  936. //Our callback that tell us when the asset open request finishes
  937. void Context::DataLoadDoneCallback(bool success, const AZStd::string& assetId)
  938. {
  939. //AZ_TracePrintf("Debug"," --m_numOutstandingOperations %d\n",__LINE__);
  940. --m_numOutstandingOperations;
  941. {
  942. AZ_TracePrintf("Debug", "DataLoadDoneCallback() ENTRY: loaded data for assetId %s\n", assetId.c_str());
  943. }
  944. // update data?
  945. if (success)
  946. {
  947. auto documentInfoIter = FindDocumentInfo(assetId);
  948. AZ_Assert(documentInfoIter.has_value(), "Invalid document lookup.");
  949. auto& documentInfo = documentInfoIter.value()->second;
  950. AZ_TracePrintf(LUAEditorDebugName, "DataLoadDoneCallback() sending OnDocumentInfoUpdated data for assetId '%s' '%s'\n", assetId.c_str(), documentInfo.m_assetName.c_str());
  951. documentInfo.m_bDataIsLoaded = true;
  952. documentInfo.m_bIsModified = false;
  953. if (m_pLUAEditorMainWindow)
  954. {
  955. m_pLUAEditorMainWindow->OnDocumentInfoUpdated(documentInfo);
  956. }
  957. }
  958. AZ_TracePrintf(LUAEditorDebugName, "DataLoadDoneCallback() EXIT\n");
  959. }
  960. void Context::DataSaveDoneCallback(bool success, const AZStd::string& assetId)
  961. {
  962. --m_numOutstandingOperations;
  963. AZ_TracePrintf(LUAEditorDebugName, "DataSaveDoneCallback() ENTRY: data save returned for assetId %s (%s)\n", assetId.c_str(), success ? "TRUE" : "FALSE");
  964. auto documentInfoIter = FindDocumentInfo(assetId);
  965. if (!documentInfoIter.has_value())
  966. {
  967. AZ_TracePrintf(LUAEditorDebugName, "DataSaveDoneCallback EXIT: no such assetId %s\n", assetId.c_str());
  968. return;
  969. }
  970. auto& documentInfo = documentInfoIter.value()->second;
  971. documentInfo.m_bIsBeingSaved = false; // we are no longer saving - regardless of whether we succeeded or not!
  972. // update data:
  973. if (success)
  974. {
  975. documentInfo.m_bDataIsWritten = true;
  976. //update the mod time in the document info
  977. if (m_fileIO)
  978. {
  979. uint64_t modTime = m_fileIO->ModificationTime(assetId.c_str());
  980. documentInfo.m_lastKnownModTime.dwHighDateTime = static_cast<DWORD>(modTime >> 32);
  981. documentInfo.m_lastKnownModTime.dwLowDateTime = static_cast<DWORD>(modTime);
  982. documentInfo.m_bDataIsLoaded = true;
  983. documentInfo.m_bIsModified = false;
  984. }
  985. // refresh source info:
  986. if (m_pLUAEditorMainWindow)
  987. {
  988. m_pLUAEditorMainWindow->OnDocumentInfoUpdated(documentInfo);
  989. }
  990. if (documentInfo.m_bCloseAfterSave)
  991. {
  992. Context_DocumentManagement::Bus::Broadcast(&Context_DocumentManagement::Bus::Events::OnCloseDocument, assetId);
  993. }
  994. }
  995. }
  996. void Context::NotifyDocumentModified(const AZStd::string& assetId, bool modified)
  997. {
  998. // the document was modified, note this down
  999. auto documentInfoIter = FindDocumentInfo(assetId);
  1000. AZ_Assert(documentInfoIter.has_value(), "Invalid document lookup.");
  1001. DocumentInfo& documentInfo = documentInfoIter.value()->second;
  1002. documentInfo.m_bIsModified = modified;
  1003. }
  1004. void Context::UpdateDocumentData(const AZStd::string& assetId, const char* dataPtr, const AZStd::size_t dataLength)
  1005. {
  1006. auto documentInfoIter = FindDocumentInfo(assetId);
  1007. AZ_Assert(documentInfoIter.has_value(), "Invalid document lookup.");
  1008. DocumentInfo& documentInfo = documentInfoIter.value()->second;
  1009. AZ_Assert(documentInfo.m_bDataIsLoaded, "You may not retrieve data until it is loaded.");
  1010. documentInfo.m_scriptAsset = AZStd::string(dataPtr, dataLength);
  1011. }
  1012. void Context::GetDocumentData(const AZStd::string& assetId, const char** dataPtr, AZStd::size_t& dataLength)
  1013. {
  1014. auto documentInfoIter = FindDocumentInfo(assetId);
  1015. AZ_Assert(documentInfoIter.has_value(), "Invalid document lookup.");
  1016. DocumentInfo& documentInfo = documentInfoIter.value()->second;
  1017. AZ_Assert(documentInfo.m_bDataIsLoaded, "You may not retrieve data until it is loaded.");
  1018. *dataPtr = documentInfo.m_scriptAsset.data();
  1019. dataLength = documentInfo.m_scriptAsset.size();
  1020. }
  1021. void Context::PerforceStatResponseCallback(bool success, const AzToolsFramework::SourceControlFileInfo& fileInfo, const AZStd::string& assetId)
  1022. {
  1023. AZ_TracePrintf("Debug", "PerforceStatResponseCallback() ENTRY: loaded assetId %s\n", assetId.c_str());
  1024. //AZ_TracePrintf("Debug"," --m_numOutstandingOperations %d\n",__LINE__);
  1025. --m_numOutstandingOperations;
  1026. // you got a callback from the perforce API, this is guaranteed to be on the main thread.
  1027. auto documentInfoIter = FindDocumentInfo(assetId);
  1028. // the document may have already been closed. this is fine.
  1029. if (!documentInfoIter.has_value())
  1030. {
  1031. AZ_TracePrintf("Debug", "PerforceStatResponseCallback() EXIT: no such assetId %s\n", assetId.c_str());
  1032. return;
  1033. }
  1034. DocumentInfo& documentInfo = documentInfoIter.value()->second;
  1035. //only means stats has been retrieved at least once
  1036. documentInfo.m_bSourceControl_Ready = true;
  1037. //this operation is now considered done
  1038. documentInfo.m_bSourceControl_BusyGettingStats = false;
  1039. //check file info flags to see if we can write
  1040. documentInfo.m_bSourceControl_CanWrite = (fileInfo.m_flags & AzToolsFramework::SCF_Writeable) != 0;
  1041. documentInfo.m_sourceControlInfo = fileInfo;
  1042. //if we can check out is slightly a little more complicated
  1043. //if the stat operation failed then we cant check out
  1044. //if the stat operation succeeded then we need to make sure that it is currently checked in and its not out of date
  1045. if (success == false)
  1046. {
  1047. documentInfo.m_bSourceControl_CanCheckOut = false;
  1048. }
  1049. else
  1050. {
  1051. documentInfo.m_bSourceControl_CanCheckOut = (fileInfo.IsManaged() && !(fileInfo.m_flags & AzToolsFramework::SCF_OutOfDate));
  1052. documentInfo.m_bSourceControl_CanCheckOut = fileInfo.m_flags & AzToolsFramework::SCF_MultiCheckOut || documentInfo.m_bSourceControl_CanCheckOut;
  1053. }
  1054. AZ_TracePrintf(LUAEditorDebugName, "PerforceStatResponseCallback() sending OnDocumentInfoUpdated\n");
  1055. if (m_pLUAEditorMainWindow)
  1056. {
  1057. m_pLUAEditorMainWindow->OnDocumentInfoUpdated(documentInfo);
  1058. }
  1059. AZ_TracePrintf("Debug", "PerforceStatResponseCallback() EXIT: OK %s\n", assetId.c_str());
  1060. }
  1061. void Context::PerforceRequestEditCallback(bool success, const AzToolsFramework::SourceControlFileInfo& fileInfo, const AZStd::string& assetId)
  1062. {
  1063. --m_numOutstandingOperations;
  1064. // you got a callback from the perforce API, this is guaranteed to be on the main thread.
  1065. auto documentInfoIter = FindDocumentInfo(assetId);
  1066. if (!documentInfoIter.has_value())
  1067. {
  1068. AZ_TracePrintf(LUAEditorDebugName, "PerforceRequestEditCallback EXIT: no such assetId %s\n", assetId.c_str());
  1069. return;
  1070. }
  1071. DocumentInfo& documentInfo = documentInfoIter.value()->second;
  1072. //this operation is considered done
  1073. documentInfo.m_bSourceControl_BusyRequestingEdit = false;
  1074. //check file info flags to see if we can write
  1075. documentInfo.m_bSourceControl_CanWrite = !fileInfo.IsReadOnly();
  1076. documentInfo.m_sourceControlInfo = fileInfo;
  1077. //if we can check out is slightly a little more complicated
  1078. //if the stat operation failed then we cant check out
  1079. //if the stat operation succeeded then we need to make sure that it is currently checked in and its not out of date
  1080. if (success == false)
  1081. {
  1082. documentInfo.m_bSourceControl_CanCheckOut = false;
  1083. }
  1084. else
  1085. {
  1086. documentInfo.m_bSourceControl_CanCheckOut = fileInfo.IsManaged() && !fileInfo.HasFlag(AzToolsFramework::SCF_OutOfDate);
  1087. documentInfo.m_bSourceControl_CanCheckOut = fileInfo.HasFlag(AzToolsFramework::SCF_MultiCheckOut) || documentInfo.m_bSourceControl_CanCheckOut;
  1088. }
  1089. if (!documentInfo.m_bSourceControl_Ready)
  1090. {
  1091. QMessageBox::warning(m_pLUAEditorMainWindow, "Warning", "Perforce shows that it's not ready.");
  1092. }
  1093. if (!documentInfo.m_bSourceControl_CanWrite)
  1094. {
  1095. if (!documentInfo.m_sourceControlInfo.HasFlag(AzToolsFramework::SCF_OpenByUser))
  1096. {
  1097. QMessageBox::warning(m_pLUAEditorMainWindow, "Warning", "This file is ReadOnly you cannot write to this file.");
  1098. }
  1099. }
  1100. else if (!documentInfo.m_bSourceControl_CanCheckOut)
  1101. {
  1102. if (documentInfo.m_sourceControlInfo.m_status == AzToolsFramework::SCS_ProviderIsDown)
  1103. {
  1104. QMessageBox::warning(m_pLUAEditorMainWindow, "Warning", "Perforce Is Down.\nFile will be saved.\nYou must reconcile with Perforce later!");
  1105. }
  1106. else if (documentInfo.m_sourceControlInfo.m_status == AzToolsFramework::SCS_ProviderError)
  1107. {
  1108. QMessageBox::warning(m_pLUAEditorMainWindow, "Warning", "Perforce encountered an error.\nFile will be saved.\nYou must reconcile with Perforce later!");
  1109. }
  1110. else if (documentInfo.m_sourceControlInfo.m_status == AzToolsFramework::SCS_CertificateInvalid)
  1111. {
  1112. QMessageBox::warning(m_pLUAEditorMainWindow, "Warning", "Perforce Connection is not trusted.\nFile will be saved.\nYou must reconcile with Perforce later!");
  1113. }
  1114. else if (!documentInfo.m_sourceControlInfo.HasFlag(AzToolsFramework::SCF_OpenByUser))
  1115. {
  1116. QMessageBox::warning(m_pLUAEditorMainWindow, "Warning", "Perforce says that you cannot write to this file.");
  1117. }
  1118. }
  1119. if (m_pLUAEditorMainWindow)
  1120. {
  1121. m_pLUAEditorMainWindow->OnDocumentInfoUpdated(documentInfo);
  1122. }
  1123. }
  1124. void Context::OnReloadDocument(const AZStd::string assetId)
  1125. {
  1126. auto documentInfoIter = FindDocumentInfo(assetId);
  1127. AZ_TracePrintf(LUAEditorDebugName, "OnReloadDocument() ENTRY user queing reload for assetId '%s'\n", assetId.c_str());
  1128. AZ_Assert(documentInfoIter.has_value(), "Invalid document lookup.");
  1129. DocumentInfo& documentInfo = documentInfoIter.value()->second;
  1130. documentInfo.m_bDataIsLoaded = false;
  1131. if (m_pLUAEditorMainWindow)
  1132. {
  1133. m_pLUAEditorMainWindow->OnDocumentInfoUpdated(documentInfo);
  1134. }
  1135. AZ_TracePrintf(LUAEditorDebugName, "OnReloadDocument() Beginning asset load.\n");
  1136. // while we're reading it, fetch the perforce information for it:
  1137. // AZ_TracePrintf("Debug"," ++m_numOutstandingOperations %d\n",__LINE__);
  1138. ++m_numOutstandingOperations;
  1139. using SCCommandBus = AzToolsFramework::SourceControlCommandBus;
  1140. SCCommandBus::Broadcast(&SCCommandBus::Events::GetFileInfo,
  1141. assetId.c_str(),
  1142. AZStd::bind(&Context::PerforceStatResponseCallback,
  1143. this,
  1144. AZStd::placeholders::_1,
  1145. AZStd::placeholders::_2,
  1146. assetId)
  1147. );
  1148. AZ_TracePrintf(LUAEditorDebugName, "OnReloadDocument() Queuing bind bus relay.\n");
  1149. ++m_numOutstandingOperations;
  1150. // Load the document
  1151. bool isLoaded = false;
  1152. AZ::IO::SystemFile luaFile;
  1153. if (luaFile.Open(assetId.c_str(), AZ::IO::SystemFile::SF_OPEN_READ_ONLY))
  1154. {
  1155. documentInfo.m_scriptAsset.resize(luaFile.Length());
  1156. luaFile.Read(documentInfo.m_scriptAsset.size(), documentInfo.m_scriptAsset.data());
  1157. isLoaded = true;
  1158. luaFile.Close();
  1159. }
  1160. Context::DataLoadDoneCallback(isLoaded, documentInfo.m_assetId);
  1161. }
  1162. void Context::OpenMostRecentDocumentView()
  1163. {
  1164. m_queuedOpenRecent = false;
  1165. if (mostRecentlyOpenedDocumentView.empty())
  1166. {
  1167. return;
  1168. }
  1169. if (m_pLUAEditorMainWindow)
  1170. {
  1171. m_bProcessingActivate = true;
  1172. m_pLUAEditorMainWindow->IgnoreFocusEvents(false);
  1173. m_pLUAEditorMainWindow->setAnimated(false);
  1174. ProvisionalShowAndFocus();
  1175. m_pLUAEditorMainWindow->OnRequestFocusView(mostRecentlyOpenedDocumentView);
  1176. m_pLUAEditorMainWindow->setAnimated(true);
  1177. m_bProcessingActivate = false;
  1178. }
  1179. }
  1180. void Context::OpenAssetByPhysicalPath(const char* physicalPath)
  1181. {
  1182. //error check input
  1183. if (!physicalPath)
  1184. {
  1185. AZ_Warning("LUAEditor::Context", false, AZStd::string::format("<span severity=\"err\">Path is null: '%s'</span>", physicalPath).c_str());
  1186. return;
  1187. }
  1188. if (!strlen(physicalPath))
  1189. {
  1190. AZ_Warning("LUAEditor::Context", false, AZStd::string::format("<span severity=\"err\">Path is empty: '%s'</span>", physicalPath).c_str());
  1191. return;
  1192. }
  1193. if (!m_fileIO->Exists(physicalPath))
  1194. {
  1195. AZ_Warning(LUAEditorInfoName, false, AZStd::string::format("<span severity=\"err\">Could not open the file, file not found: '%s'</span>", physicalPath).c_str());
  1196. QMessageBox msgBox(m_pLUAEditorMainWindow);
  1197. msgBox.setModal(true);
  1198. msgBox.setText("File not found");
  1199. msgBox.setInformativeText(physicalPath);
  1200. msgBox.setStandardButtons(QMessageBox::Ok);
  1201. msgBox.setDefaultButton(QMessageBox::Ok);
  1202. msgBox.setIcon(QMessageBox::Critical);
  1203. msgBox.exec();
  1204. CleanUpBreakpoints();
  1205. return;
  1206. }
  1207. AssetOpenRequested(physicalPath, true);
  1208. }
  1209. void Context::AssetOpenRequested(const AZStd::string& assetId, bool errorOnNotFound)
  1210. {
  1211. if (!m_fileIO)
  1212. {
  1213. return;
  1214. }
  1215. AZStd::string normalizedAssetId = assetId;
  1216. AZStd::to_lower(normalizedAssetId.begin(), normalizedAssetId.end());
  1217. ShowLUAEditorView();
  1218. // asset browser requests opening of a particular assets.
  1219. // we need to do a whole bunch of things
  1220. // * we need to start tracking and validate the document that is about to be opened. It might already be open, for example.
  1221. // - documents may belong to another Context (?) like for example if entities have embedded blobs of lua. In that case, the
  1222. // interface may be different in that the asset is treated as a different type and the other Context manages the docs.
  1223. // * we need to create a new LUA panel for it
  1224. // * we need to load that lua panel with the document's data, initializing it.
  1225. // are we already tracking it?
  1226. auto document = FindDocumentInfo(assetId);
  1227. if (document.has_value())
  1228. {
  1229. // tell the view that it needs to focus that document!
  1230. mostRecentlyOpenedDocumentView = normalizedAssetId;
  1231. if (m_queuedOpenRecent)
  1232. {
  1233. return;
  1234. }
  1235. AZ::SystemTickBus::QueueFunction(&Context::OpenMostRecentDocumentView, this);
  1236. return;
  1237. }
  1238. if (!m_fileIO->Exists(assetId.c_str()))
  1239. {
  1240. if (errorOnNotFound)
  1241. {
  1242. AZ_Warning(LUAEditorInfoName, false, AZStd::string::format("<span severity=\"err\">Could not open the file, file not found: '%s'</span>", assetId.c_str()).c_str());
  1243. QMessageBox msgBox(m_pLUAEditorMainWindow);
  1244. msgBox.setModal(true);
  1245. msgBox.setText("File not found");
  1246. msgBox.setStandardButtons(QMessageBox::Ok);
  1247. msgBox.setDefaultButton(QMessageBox::Ok);
  1248. msgBox.setIcon(QMessageBox::Critical);
  1249. msgBox.exec();
  1250. }
  1251. CleanUpBreakpoints();
  1252. return;
  1253. }
  1254. // Register the script into the asset catalog
  1255. AZ::Data::AssetType assetType = AZ::AzTypeInfo<AZ::ScriptAsset>::Uuid();
  1256. AZ::Data::AssetId catalogAssetId;
  1257. AZ::Data::AssetCatalogRequestBus::BroadcastResult(
  1258. catalogAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, normalizedAssetId.c_str(), assetType, true);
  1259. uint64_t modTime = m_fileIO->ModificationTime(assetId.c_str());
  1260. DocumentInfo info;
  1261. info.m_assetName = assetId;
  1262. AZ::StringFunc::Path::GetFullFileName(assetId.c_str(), info.m_displayName);
  1263. info.m_assetId = normalizedAssetId;
  1264. info.m_bSourceControl_BusyGettingStats = true;
  1265. info.m_bSourceControl_BusyGettingStats = false;
  1266. info.m_bSourceControl_CanWrite = true;
  1267. info.m_lastKnownModTime.dwHighDateTime = static_cast<DWORD>(modTime >> 32);
  1268. info.m_lastKnownModTime.dwLowDateTime = static_cast<DWORD>(modTime);
  1269. info.m_bIsModified = false;
  1270. // load the script source
  1271. auto infoIter = m_documentInfoMap.insert(AZStd::make_pair(info.m_assetId, info)).first;
  1272. // now open a view that will end up with its info (the view will have a progress bar on it as it loads)
  1273. m_pLUAEditorMainWindow->OnOpenLUAView(info);
  1274. {
  1275. // while we're reading it, fetch the perforce information for it:
  1276. //AZ_TracePrintf("Debug"," ++m_numOutstandingOperations %d\n",__LINE__);
  1277. ++m_numOutstandingOperations;
  1278. using SCCommandBus = AzToolsFramework::SourceControlCommandBus;
  1279. SCCommandBus::Broadcast(&SCCommandBus::Events::GetFileInfo,
  1280. assetId.c_str(),
  1281. AZStd::bind(&Context::PerforceStatResponseCallback,
  1282. this,
  1283. AZStd::placeholders::_1,
  1284. AZStd::placeholders::_2,
  1285. assetId)
  1286. );
  1287. }
  1288. //AZ_TracePrintf("Debug"," ++m_numOutstandingOperations %d\n",__LINE__);
  1289. ++m_numOutstandingOperations;
  1290. //////////////////////////////////////////////////////////////////////////
  1291. // Load in places
  1292. bool isLoaded = false;
  1293. AZ::IO::SystemFile luaFile;
  1294. if (luaFile.Open(assetId.c_str(), AZ::IO::SystemFile::SF_OPEN_READ_ONLY))
  1295. {
  1296. infoIter->second.m_scriptAsset.resize(luaFile.Length());
  1297. luaFile.Read(infoIter->second.m_scriptAsset.size(), infoIter->second.m_scriptAsset.data());
  1298. isLoaded = true;
  1299. luaFile.Close();
  1300. }
  1301. DataLoadDoneCallback(isLoaded, normalizedAssetId);
  1302. //////////////////////////////////////////////////////////////////////////
  1303. if (m_queuedOpenRecent)
  1304. {
  1305. return;
  1306. }
  1307. if (m_pLUAEditorMainWindow)
  1308. {
  1309. m_pLUAEditorMainWindow->IgnoreFocusEvents(false);
  1310. }
  1311. mostRecentlyOpenedDocumentView = normalizedAssetId;
  1312. AZ::SystemTickBus::QueueFunction(&Context::OpenMostRecentDocumentView, this);
  1313. }
  1314. void Context::RunAsAnotherInstance()
  1315. {
  1316. const AZStd::string k_luaScriptFileString = "files";
  1317. const AzFramework::CommandLine* commandLine = nullptr;
  1318. AZ::ComponentApplicationBus::Broadcast([&commandLine](AZ::ComponentApplicationRequests* requests)
  1319. {
  1320. commandLine = requests->GetAzCommandLine();
  1321. });
  1322. AZStd::string parameters = "";
  1323. size_t numSwitchValues = commandLine->GetNumSwitchValues(k_luaScriptFileString);
  1324. if (numSwitchValues >= 1)
  1325. {
  1326. for (size_t i = 0; i < numSwitchValues; ++i)
  1327. {
  1328. AZStd::string inputValue = commandLine->GetSwitchValue(k_luaScriptFileString, i);
  1329. AZStd::to_lower(const_cast<AZStd::string&>(inputValue).begin(), const_cast<AZStd::string&>(inputValue).end());
  1330. // Cache the files we want to open, we will open them when we activate the main window.
  1331. parameters.append(inputValue);
  1332. parameters.append(";");
  1333. }
  1334. }
  1335. // Send the list of files to open to the running instance.
  1336. LegacyFramework::IPCCommandBus::Broadcast(
  1337. &LegacyFramework::IPCCommandBus::Events::SendIPCCommand, "open_files", parameters.c_str());
  1338. }
  1339. void Context::ShowLUAEditorView()
  1340. {
  1341. ProvisionalShowAndFocus(true);
  1342. }
  1343. //////////////////////////////////////////////////////////////////////////
  1344. // Context_DebuggerManagement Messages
  1345. //////////////////////////////////////////////////////////////////////////
  1346. // ExecuteScriptBlob - execute a script blob.
  1347. void Context::ExecuteScriptBlob(const AZStd::string& fromAssetId, bool executeLocally)
  1348. {
  1349. auto documentInfoIter = FindDocumentInfo(fromAssetId);
  1350. AZ_Assert(documentInfoIter.has_value(), "Could not find data");
  1351. auto& documentInfo = documentInfoIter.value()->second;
  1352. if (documentInfo.m_scriptAsset.empty())
  1353. {
  1354. AZ_Warning(LUAEditorDebugName, false, "Could not execute empty script document.");
  1355. return;
  1356. }
  1357. const char* scriptData = documentInfo.m_scriptAsset.c_str();
  1358. // the debug name is simply the name of the document.
  1359. // if its unnamed, it's synthesized
  1360. AZStd::string debugName = documentInfo.m_assetName;
  1361. LUAEditor::LUAStackTrackerMessages::Bus::Broadcast(&LUAEditor::LUAStackTrackerMessages::Bus::Events::StackClear);
  1362. SynchronizeBreakpoints();
  1363. // if we're executing it locally, we'll just execute it locally - do not involve the debugger.
  1364. if (executeLocally)
  1365. {
  1366. AZ::ScriptContext* sc = nullptr;
  1367. AZ::ScriptSystemRequestBus::BroadcastResult(
  1368. sc, &AZ::ScriptSystemRequestBus::Events::GetContext, AZ::ScriptContextIds::DefaultScriptContextId);
  1369. if (sc)
  1370. {
  1371. // we might want to bracket this with some sort of error or warning protection, to collect
  1372. // the error / warning results in a neat way to show user.
  1373. sc->Execute(scriptData, debugName.c_str());
  1374. }
  1375. return;
  1376. }
  1377. // otherwise we've been told to execute it on the debugger remotely which is presently unsupported
  1378. }
  1379. void Context::SynchronizeBreakpoints()
  1380. {
  1381. //AZ_TracePrintf(LUAEditorDebugName, "Context::SynchronizeBreakpoints()\n");
  1382. for (BreakpointMap::iterator it = m_pBreakpointSavedState->m_Breakpoints.begin(); it != m_pBreakpointSavedState->m_Breakpoints.end(); ++it)
  1383. {
  1384. Breakpoint& bp = it->second;
  1385. LUAEditorDebuggerMessages::Bus::Broadcast(
  1386. &LUAEditorDebuggerMessages::Bus::Events::CreateBreakpoint, bp.m_assetName, bp.m_documentLine);
  1387. }
  1388. LUABreakpointTrackerMessages::Bus::Broadcast(
  1389. &LUABreakpointTrackerMessages::Bus::Events::BreakpointsUpdate, m_pBreakpointSavedState->m_Breakpoints);
  1390. }
  1391. void Context::CreateBreakpoint(const AZStd::string& fromAssetId, int lineNumber)
  1392. {
  1393. auto info = FindDocumentInfo(fromAssetId);
  1394. AZ_Assert(info.has_value(), "Invalid document lookup.");
  1395. DocumentInfo& refInfo = info.value()->second;
  1396. AZ::Uuid breakpointUID = AZ::Uuid::CreateRandom();
  1397. // when a breakpoint is created, we need to find out what the most recent blob is that was applied
  1398. // to patch over that line number in that document, and apply it to that blob.
  1399. // first, let's find if we've patched or run any blobs. By default, the doc name will be the asset name.
  1400. AZStd::string debugName = refInfo.m_assetName;
  1401. AZ_TracePrintf(LUAEditorDebugName, "Context::CreateBreakpoint( %s )\n", debugName.c_str());
  1402. BreakpointMap::pair_iter_bool newInsertion = m_pBreakpointSavedState->m_Breakpoints.insert(breakpointUID);
  1403. AZ_Assert(newInsertion.second, "Breakpoint already exists!");
  1404. Breakpoint& newBreakpoint = newInsertion.first->second;
  1405. newBreakpoint.m_assetName = debugName;
  1406. newBreakpoint.m_breakpointId = breakpointUID;
  1407. newBreakpoint.m_assetId = info.value()->first;
  1408. newBreakpoint.m_documentLine = lineNumber;
  1409. // we now know the 'debug name' (a string) that was submitted to the piece of code that this breakpoint is for, and we know the line number
  1410. // inside that blob that the breakpoint wants to be set on.
  1411. LUAEditorDebuggerMessages::Bus::Broadcast(&LUAEditorDebuggerMessages::Bus::Events::CreateBreakpoint, debugName, lineNumber);
  1412. //AZ_TracePrintf(LUAEditorDebugName, "Setting breakpoint in '%s' on line %i (In document %s line %i)\n", debugName.c_str(), lineNumber, doc.m_displayName.c_str(), lineNumber);
  1413. LUABreakpointTrackerMessages::Bus::Broadcast(
  1414. &LUABreakpointTrackerMessages::Bus::Events::BreakpointsUpdate, m_pBreakpointSavedState->m_Breakpoints);
  1415. }
  1416. void Context::DeleteBreakpoint(const AZ::Uuid& breakpointUID)
  1417. {
  1418. //AZ_TracePrintf(LUAEditorDebugName, "Context::DeleteBreakpoint()\n");
  1419. BreakpointMap::iterator finder = m_pBreakpointSavedState->m_Breakpoints.find(breakpointUID);
  1420. //AZ_Assert(finder != m_breakpoints.end(), "Breakpoint referred to, but not found!");
  1421. if (finder != m_pBreakpointSavedState->m_Breakpoints.end())
  1422. {
  1423. Breakpoint& bp = finder->second;
  1424. AZ_TracePrintf(LUAEditorDebugName, " - Removed breakpoint in '%s' on line '%i'\n", bp.m_assetName.c_str(), bp.m_documentLine);
  1425. LUAEditorDebuggerMessages::Bus::Broadcast(
  1426. &LUAEditorDebuggerMessages::Bus::Events::RemoveBreakpoint, bp.m_assetName, bp.m_documentLine);
  1427. m_pBreakpointSavedState->m_Breakpoints.erase(finder);
  1428. LUABreakpointTrackerMessages::Bus::Broadcast(
  1429. &LUABreakpointTrackerMessages::Bus::Events::BreakpointsUpdate, m_pBreakpointSavedState->m_Breakpoints);
  1430. }
  1431. }
  1432. // Find any breakpoints that no longer have any attachment and remove them
  1433. void Context::CleanUpBreakpoints()
  1434. {
  1435. // Build a list of orphaned breakpoints
  1436. std::vector<AZ::Uuid> invalidBreakpoints;
  1437. for (auto const &breakpoint : m_pBreakpointSavedState->m_Breakpoints)
  1438. {
  1439. if (!m_fileIO->Exists(breakpoint.second.m_assetName.c_str()))
  1440. {
  1441. invalidBreakpoints.emplace_back(breakpoint.second.m_breakpointId);
  1442. }
  1443. }
  1444. for (auto const &id : invalidBreakpoints)
  1445. {
  1446. DeleteBreakpoint(id);
  1447. }
  1448. // submit the updated list
  1449. LUABreakpointTrackerMessages::Bus::Broadcast(
  1450. &LUABreakpointTrackerMessages::Bus::Events::BreakpointsUpdate, m_pBreakpointSavedState->m_Breakpoints);
  1451. }
  1452. //TODO: add a valid-invalid state to local breakpoints
  1453. // default new breaks to INVALID
  1454. // use the response here to set matching breaks to VALID
  1455. void Context::OnBreakpointAdded(const AZStd::string& /*assetIdString*/, int /*lineNumber*/)
  1456. {
  1457. //AZ_TracePrintf(LUAEditorDebugName, "Debug agent added breakpoint at %s, line %d.\n", assetIdString.c_str(), lineNumber + 1);
  1458. }
  1459. void Context::OnBreakpointRemoved(const AZStd::string& /*assetIdString*/, int /*lineNumber*/)
  1460. {
  1461. //AZ_TracePrintf(LUAEditorDebugName, "Debug agent removed breakpoint at %s, line %d.\n", assetIdString.c_str(), lineNumber + 1);
  1462. }
  1463. void Context::MoveBreakpoint(const AZ::Uuid& breakpointUID, int lineNumber)
  1464. {
  1465. // moving a breakpoint will cause it to update where it is in the document in question
  1466. // however, we don't actually re-transmit the breakpoint over the wire, because we haven't re-run the script
  1467. // this is just housekeeping so that when the network says a certain breakpoint came in at a certain place,
  1468. // we know what they're talking about.
  1469. if (lineNumber >= 0)
  1470. {
  1471. BreakpointMap::iterator finder = m_pBreakpointSavedState->m_Breakpoints.find(breakpointUID);
  1472. if (finder != m_pBreakpointSavedState->m_Breakpoints.end())
  1473. {
  1474. Breakpoint& bp = finder->second;
  1475. bp.m_documentLine = lineNumber;
  1476. AZ_TracePrintf(LUAEditorDebugName, "Breakpoint '%s' updated to point at document line '%i'\n", bp.m_assetName.c_str(), bp.m_documentLine);
  1477. }
  1478. // send out the update
  1479. LUABreakpointTrackerMessages::Bus::Broadcast(
  1480. &LUABreakpointTrackerMessages::Bus::Events::BreakpointsUpdate, m_pBreakpointSavedState->m_Breakpoints);
  1481. }
  1482. }
  1483. void Context::OnDebuggerAttached()
  1484. {
  1485. //AZ_TracePrintf(LUAEditorDebugName, "Context::OnDebuggerAttached()\n");
  1486. Context_ControlManagement::Bus::Broadcast(&Context_ControlManagement::Bus::Events::OnDebuggerAttached);
  1487. LUAEditorDebuggerMessages::Bus::Broadcast(
  1488. &LUAEditorDebuggerMessages::Bus::Events::EnumRegisteredClasses, m_currentTargetContext.c_str());
  1489. LUAEditorDebuggerMessages::Bus::Broadcast(
  1490. &LUAEditorDebuggerMessages::Bus::Events::EnumRegisteredEBuses, m_currentTargetContext.c_str());
  1491. LUAEditorDebuggerMessages::Bus::Broadcast(
  1492. &LUAEditorDebuggerMessages::Bus::Events::EnumRegisteredGlobals, m_currentTargetContext.c_str());
  1493. LUAEditorMainWindowMessages::Bus::Broadcast(&LUAEditorMainWindowMessages::Bus::Events::OnConnectedToDebugger);
  1494. LUAWatchesDebuggerMessages::Bus::Broadcast(&LUAWatchesDebuggerMessages::Bus::Events::OnDebuggerAttached);
  1495. SynchronizeBreakpoints();
  1496. }
  1497. void Context::OnDebuggerRefused()
  1498. {
  1499. //AZ_TracePrintf(LUAEditorDebugName, "Context::OnDebuggerRefused()\n");
  1500. LUAEditorMainWindowMessages::Bus::Broadcast(&LUAEditorMainWindowMessages::Bus::Events::OnDisconnectedFromDebugger);
  1501. Context_ControlManagement::Bus::Broadcast(&Context_ControlManagement::Bus::Events::OnDebuggerDetached);
  1502. LUAWatchesDebuggerMessages::Bus::Broadcast(&LUAWatchesDebuggerMessages::Bus::Events::OnDebuggerDetached);
  1503. }
  1504. void Context::OnDebuggerDetached()
  1505. {
  1506. //AZ_TracePrintf(LUAEditorDebugName, "Context::OnDebuggerDetached()\n");
  1507. LUAEditorMainWindowMessages::Bus::Broadcast(&LUAEditorMainWindowMessages::Bus::Events::OnDisconnectedFromDebugger);
  1508. Context_ControlManagement::Bus::Broadcast(&Context_ControlManagement::Bus::Events::OnDebuggerDetached);
  1509. LUAWatchesDebuggerMessages::Bus::Broadcast(&LUAWatchesDebuggerMessages::Bus::Events::OnDebuggerDetached);
  1510. }
  1511. // this happens when a breakpoint is hit:
  1512. void Context::OnBreakpointHit(const AZStd::string& relativePath, int lineNumber)
  1513. {
  1514. // Convert from debug path (relative) to absolute path (how the Lua IDE stores files)
  1515. AZStd::string absolutePath;
  1516. AZStd::string formattedRelativePath = relativePath.substr(1);
  1517. AzToolsFramework::AssetSystemRequestBus::Broadcast(
  1518. &AzToolsFramework::AssetSystemRequestBus::Events::GetFullSourcePathFromRelativeProductPath,
  1519. formattedRelativePath,
  1520. absolutePath);
  1521. // If finding a .lua fails, attempt the equivalent .luac
  1522. if (absolutePath.empty() && relativePath.ends_with(".lua"))
  1523. {
  1524. formattedRelativePath = relativePath.substr(1) + "c";
  1525. AzToolsFramework::AssetSystemRequestBus::Broadcast(
  1526. &AzToolsFramework::AssetSystemRequestBus::Events::GetFullSourcePathFromRelativeProductPath,
  1527. formattedRelativePath,
  1528. absolutePath);
  1529. }
  1530. //AZ_TracePrintf(LUAEditorDebugName, "Breakpoint '%s' was hit on line %i\n", assetIdString.c_str(), lineNumber);
  1531. LUAEditorDebuggerMessages::Bus::Broadcast(&LUAEditorDebuggerMessages::Bus::Events::GetCallstack);
  1532. LUAEditor::LUABreakpointRequestMessages::Bus::Broadcast(
  1533. &LUAEditor::LUABreakpointRequestMessages::Bus::Events::RequestEditorFocus, absolutePath, lineNumber);
  1534. AZStd::string assetId = absolutePath;
  1535. // let's see if we can find an open document
  1536. auto documentIterator = FindDocumentInfo(assetId);
  1537. if (!documentIterator.has_value())
  1538. {
  1539. // the document might have been closed
  1540. AssetOpenRequested(assetId, true);
  1541. // let's see if we can find an open document
  1542. auto requestedDocumentIterator = FindDocumentInfo(assetId);
  1543. if (requestedDocumentIterator.has_value())
  1544. {
  1545. requestedDocumentIterator.value()->second.m_PresetLineAtOpen = lineNumber;
  1546. }
  1547. // early out after requesting a background data load
  1548. return;
  1549. }
  1550. int actualDocumentLineNumber = lineNumber;
  1551. // we now know what document that the breakpoint is talking about.
  1552. // we could just assume that the document has not changed since we saw the breakpoint applied, but its possible that is has in fact shifted.
  1553. // do we have any breakpoints established for that particular blob?
  1554. {
  1555. BreakpointMap::iterator it = m_pBreakpointSavedState->m_Breakpoints.begin();
  1556. for (; it != m_pBreakpointSavedState->m_Breakpoints.end(); ++it)
  1557. {
  1558. const Breakpoint& bp = it->second;
  1559. if (bp.m_assetId == assetId)
  1560. {
  1561. if (bp.m_documentLine == lineNumber)
  1562. {
  1563. // this is that breakpoint!
  1564. actualDocumentLineNumber = bp.m_documentLine; // bump it if its shifted:
  1565. LUABreakpointTrackerMessages::Bus::Broadcast(&LUABreakpointTrackerMessages::Bus::Events::BreakpointHit, bp);
  1566. break;
  1567. }
  1568. }
  1569. }
  1570. if (it == m_pBreakpointSavedState->m_Breakpoints.end())
  1571. {
  1572. // it's an imaginary break resulting from one of the STEP calls
  1573. // dummy up a bp and send that as the message
  1574. Breakpoint dbp;
  1575. dbp.m_breakpointId = AZ::Uuid::CreateNull();
  1576. dbp.m_assetId = "";
  1577. dbp.m_documentLine = lineNumber;
  1578. LUABreakpointTrackerMessages::Bus::Broadcast(&LUABreakpointTrackerMessages::Bus::Events::BreakpointHit, dbp);
  1579. }
  1580. }
  1581. //AZ_TracePrintf(LUAEditorDebugName, "That translates to line number %i in document '%s'\n", actualDocumentLineNumber, doc.m_displayName.c_str());
  1582. // what do we actually do?
  1583. // we need to
  1584. // 1. FOCUS that window
  1585. // 2. INDICATE that we are 'stopped'
  1586. // 3. Update any watched variables
  1587. // 4. Show a program cursor on that line!
  1588. // 5. Enable the step over and so on, the debugging controls basically.
  1589. // focus the window:
  1590. ProvisionalShowAndFocus(true);
  1591. // are we already tracking it?
  1592. // tell the view that it needs to focus that document!
  1593. m_pLUAEditorMainWindow->OnRequestFocusView(assetId);
  1594. m_pLUAEditorMainWindow->MoveProgramCursor(assetId, actualDocumentLineNumber);
  1595. }
  1596. void Context::OnReceivedAvailableContexts(const AZStd::vector<AZStd::string>& contexts)
  1597. {
  1598. //AZ_TracePrintf(LUAEditorDebugName, "Context::OnReceivedAvailableContexts()\n");
  1599. m_targetContexts = contexts;
  1600. size_t i;
  1601. for (i = 0; i < m_targetContexts.size(); ++i)
  1602. {
  1603. if (m_currentTargetContext == m_targetContexts[i])
  1604. {
  1605. break;
  1606. }
  1607. }
  1608. if (i >= m_targetContexts.size())
  1609. {
  1610. m_targetContexts.clear();
  1611. m_currentTargetContext = "Default";
  1612. }
  1613. LUAEditor::Context_ControlManagement::Bus::Broadcast(
  1614. &LUAEditor::Context_ControlManagement::Bus::Events::OnTargetContextPrepared, m_currentTargetContext);
  1615. RequestAttachDebugger();
  1616. }
  1617. void Context::OnReceivedRegisteredClasses(const AzFramework::ScriptUserClassList& classes)
  1618. {
  1619. AZ_TracePrintf(LUAEditorDebugName, "Context::OnReceivedRegisteredClasses()\n");
  1620. // Reset the class reference for the current target
  1621. ContextReference& reference = m_reference[m_currentTargetContext];
  1622. reference.m_classes = classes;
  1623. UpdateReferenceWindow();
  1624. }
  1625. void Context::OnReceivedRegisteredEBuses(const AzFramework::ScriptUserEBusList& ebuses)
  1626. {
  1627. AZ_TracePrintf(LUAEditorDebugName, "Context::OnReceivedRegisteredEBuses()\n");
  1628. ContextReference& reference = m_reference[m_currentTargetContext];
  1629. reference.m_buses = ebuses;
  1630. UpdateReferenceWindow();
  1631. }
  1632. void Context::OnReceivedRegisteredGlobals(const AzFramework::ScriptUserMethodList& methods, const AzFramework::ScriptUserPropertyList& properties)
  1633. {
  1634. AZ_TracePrintf(LUAEditorDebugName, "Context::OnReceivedRegisteredGlobals()\n");
  1635. ContextReference& reference = m_reference[m_currentTargetContext];
  1636. reference.m_globals.m_methods = methods;
  1637. reference.m_globals.m_properties = properties;
  1638. UpdateReferenceWindow();
  1639. }
  1640. AZStd::string GetTooltip(const AzFramework::ScriptUserPropertyInfo& propInfo)
  1641. {
  1642. static const char* lut[2][2] =
  1643. {
  1644. { "Locked", "WO" },
  1645. { "RO", "R/W" }
  1646. };
  1647. AZStd::string rw = lut[propInfo.m_isRead][propInfo.m_isWrite];
  1648. return propInfo.m_name + "[" + rw + "]";
  1649. }
  1650. AZStd::string GetTooltip(const AzFramework::ScriptUserMethodInfo& methodInfo)
  1651. {
  1652. return methodInfo.m_name + "(" + methodInfo.m_dbgParamInfo + ")";
  1653. }
  1654. AZStd::string GetTooltip(const AzFramework::ScriptUserClassInfo& classInfo)
  1655. {
  1656. return classInfo.m_name + "()";
  1657. }
  1658. AZStd::string GetTooltip(const AzFramework::ScriptUserEBusInfo& ebusInfo)
  1659. {
  1660. return ebusInfo.m_name;
  1661. }
  1662. void Context::UpdateReferenceWindow()
  1663. {
  1664. const ContextReference& reference = m_reference[m_currentTargetContext];
  1665. m_LUAKeywords.clear();
  1666. AddDefaultLUAKeywords();
  1667. m_LUALibraryFunctions.clear();
  1668. AddDefaultLUALibraryFunctions();
  1669. m_referenceModel->clear();
  1670. // Globals
  1671. ReferenceItem* global = aznew ReferenceItem("Globals", AZ::u64(0));
  1672. global->setToolTip("Globals");
  1673. global->setWhatsThis("Global Methods and Variables");
  1674. m_referenceModel->appendRow(global);
  1675. for (const auto& methodInfo : reference.m_globals.m_methods)
  1676. {
  1677. ReferenceItem* method = aznew ReferenceItem(methodInfo.m_name.c_str(), AZ::u64(0));
  1678. method->setToolTip(QString::fromUtf8(GetTooltip(methodInfo).c_str()));
  1679. method->setWhatsThis(QString::fromUtf8(methodInfo.m_name.c_str()));
  1680. global->appendRow(method);
  1681. m_LUALibraryFunctions.insert(methodInfo.m_name);
  1682. }
  1683. for (const auto& propInfo : reference.m_globals.m_properties)
  1684. {
  1685. ReferenceItem* variable = aznew ReferenceItem(propInfo.m_name.c_str(), AZ::u64(0));
  1686. variable->setToolTip(QString::fromUtf8(GetTooltip(propInfo).c_str()));
  1687. variable->setWhatsThis(QString::fromUtf8(propInfo.m_name.c_str()));
  1688. global->appendRow(variable);
  1689. m_LUAKeywords.insert(propInfo.m_name);
  1690. }
  1691. // Classes
  1692. ReferenceItem* classes = aznew ReferenceItem("Classes", AZ::u64(0));
  1693. classes->setToolTip("Classes");
  1694. classes->setWhatsThis("Classes");
  1695. m_referenceModel->appendRow(classes);
  1696. for (const auto& classInfo : reference.m_classes)
  1697. {
  1698. ReferenceItem* classItem = aznew ReferenceItem(QString::fromUtf8(classInfo.m_name.c_str()), 0);
  1699. classItem->setToolTip(QString::fromUtf8(classInfo.m_name.c_str()));
  1700. classItem->setWhatsThis(QString::fromUtf8(classInfo.m_name.c_str()));
  1701. classes->appendRow(classItem);
  1702. for (const auto& methodInfo : classInfo.m_methods)
  1703. {
  1704. ReferenceItem* methodItem = aznew ReferenceItem(QString::fromUtf8((methodInfo.m_name + "( " + methodInfo.m_dbgParamInfo + " )").c_str()), 0);
  1705. methodItem->setToolTip(QString::fromUtf8(GetTooltip(methodInfo).c_str()));
  1706. methodItem->setWhatsThis(QString::fromUtf8(methodInfo.m_name.c_str()));
  1707. classItem->appendRow(methodItem);
  1708. m_LUALibraryFunctions.insert(classInfo.m_name + "." + methodInfo.m_name);
  1709. }
  1710. for (const auto& propInfo : classInfo.m_properties)
  1711. {
  1712. ReferenceItem* propItem = aznew ReferenceItem(propInfo.m_name.c_str(), 0);
  1713. propItem->setToolTip(QString::fromUtf8(GetTooltip(propInfo).c_str()));
  1714. propItem->setWhatsThis(QString::fromUtf8(propInfo.m_name.c_str()));
  1715. classItem->appendRow(propItem);
  1716. m_LUALibraryFunctions.insert(classInfo.m_name + "." + propInfo.m_name);
  1717. }
  1718. }
  1719. // Buses
  1720. ReferenceItem* buses = aznew ReferenceItem("EBuses", AZ::u64(0));
  1721. buses->setToolTip("EBuses");
  1722. buses->setWhatsThis("EBuses");
  1723. m_referenceModel->appendRow(buses);
  1724. for (auto const &ebusInfo : reference.m_buses)
  1725. {
  1726. // Make a reference item from the info-block for displaying in the class hierarchy and add it to the reference table.
  1727. ReferenceItem* ebus = aznew ReferenceItem(QString::fromUtf8(ebusInfo.m_name.c_str()), 0);
  1728. ebus->setToolTip(QString::fromUtf8(GetTooltip(ebusInfo).c_str()));
  1729. ebus->setWhatsThis(QString::fromUtf8(ebusInfo.m_name.c_str()));
  1730. buses->appendRow(ebus);
  1731. if (!ebusInfo.m_events.empty())
  1732. {
  1733. ReferenceItem* eventRoot = aznew ReferenceItem("Event", 0);
  1734. ReferenceItem* broadcastRoot = (ebusInfo.m_canBroadcast) ? aznew ReferenceItem("Broadcast", 0) : nullptr;
  1735. ReferenceItem* notificationsRoot = (ebusInfo.m_hasHandler) ? aznew ReferenceItem("Notifications", 0) : nullptr;
  1736. for (auto const &eventInfo : ebusInfo.m_events)
  1737. {
  1738. // Construct the visual element for displaying in the reference pane.
  1739. ReferenceItem* eventItem = aznew ReferenceItem(QString::fromUtf8(GetTooltip(eventInfo).c_str()), 0);
  1740. eventItem->setToolTip(QString::fromUtf8(GetTooltip(eventInfo).c_str()));
  1741. eventItem->setWhatsThis(QString::fromUtf8(eventInfo.m_name.c_str()));
  1742. if (eventInfo.m_category == "Event")
  1743. {
  1744. eventRoot->appendRow(eventItem);
  1745. m_LUALibraryFunctions.insert(ebusInfo.m_name + ".Event." + eventInfo.m_name);
  1746. }
  1747. else if (eventInfo.m_category == "Broadcast" && broadcastRoot)
  1748. {
  1749. broadcastRoot->appendRow(eventItem);
  1750. m_LUALibraryFunctions.insert(ebusInfo.m_name + ".Broadcast." + eventInfo.m_name);
  1751. }
  1752. else if (eventInfo.m_category == "Notification" && notificationsRoot)
  1753. {
  1754. notificationsRoot->appendRow(eventItem);
  1755. }
  1756. else
  1757. {
  1758. // This should not happen, but in the case that we somehow have a handler or broadcast
  1759. // and nowhere to attach it, let's at least not leak it
  1760. delete eventItem;
  1761. }
  1762. }
  1763. // Add the root nodes that have children to the bus tree, delete empty roots
  1764. for (ReferenceItem* rootNode : { eventRoot, broadcastRoot, notificationsRoot })
  1765. {
  1766. if (rootNode && rootNode->rowCount() > 0)
  1767. {
  1768. ebus->appendRow(rootNode);
  1769. }
  1770. else if (rootNode)
  1771. {
  1772. delete rootNode;
  1773. }
  1774. }
  1775. }
  1776. }
  1777. if (m_pLUAEditorMainWindow)
  1778. {
  1779. Q_EMIT m_pLUAEditorMainWindow->OnReferenceDataChanged();
  1780. }
  1781. HighlightedWordNotifications::Bus::Broadcast(&HighlightedWordNotifications::Bus::Events::LUALibraryFunctionsUpdated);
  1782. }
  1783. void Context::OnReceivedLocalVariables(const AZStd::vector<AZStd::string>& vars)
  1784. {
  1785. //AZ_TracePrintf(LUAEditorDebugName, "Context::OnReceivedLocalVariables()\n");
  1786. LUALocalsTrackerMessages::Bus::Broadcast(&LUALocalsTrackerMessages::Bus::Events::LocalsUpdate, vars);
  1787. for (size_t i = 0; i < vars.size(); ++i)
  1788. {
  1789. //AZ_TracePrintf(LUAEditorDebugName, "\t%s\n", vars[i].c_str());
  1790. LUAEditorDebuggerMessages::Bus::Broadcast(&LUAEditorDebuggerMessages::Bus::Events::GetValue, vars[i]);
  1791. }
  1792. }
  1793. void Context::OnReceivedCallstack(const AZStd::vector<AZStd::string>& callstack)
  1794. {
  1795. //AZ_TracePrintf(LUAEditorDebugName, "Context::OnReceivedCallstack()\n");
  1796. LUAEditor::StackList sl;
  1797. for (size_t i = 0; i < callstack.size(); ++i)
  1798. {
  1799. //AZ_TracePrintf(LUAEditorDebugName, "stack[%d] %s\n", i, callstack[i].c_str());
  1800. const char* stackLine = callstack[i].c_str();
  1801. // strings starting with a pointer address aren't useful and break the format
  1802. if (stackLine)
  1803. {
  1804. const size_t tempSize = 4096;
  1805. if (!isdigit(stackLine[0]))
  1806. {
  1807. if (stackLine[0] == '[')
  1808. {
  1809. // LUA format
  1810. LUAEditor::StackEntry s;
  1811. const char* fb = strchr(stackLine, '@');
  1812. if (fb)
  1813. {
  1814. ++fb;
  1815. const char* fe = strchr(fb, '(');
  1816. if (fe)
  1817. {
  1818. char temp[tempSize];
  1819. --fe;
  1820. memcpy(temp, fb, fe - fb);
  1821. temp[ fe - fb ] = 0;
  1822. s.m_blob = temp;
  1823. int line = 0;
  1824. const char* ns = strchr(stackLine, '(');
  1825. if (ns)
  1826. {
  1827. ++ns;
  1828. line = atoi(ns) - 1; // -1 offset to bridge editor vs display
  1829. }
  1830. s.m_blobLine = line;
  1831. sl.push_back(s);
  1832. }
  1833. }
  1834. }
  1835. else
  1836. {
  1837. // standard VS format
  1838. LUAEditor::StackEntry s;
  1839. const char* fb = stackLine;
  1840. const char* fe = strchr(fb, '(');
  1841. if (fe)
  1842. {
  1843. char temp[tempSize];
  1844. --fe;
  1845. ptrdiff_t pdt = fe - fb;
  1846. if (pdt < (tempSize - 1))
  1847. {
  1848. memcpy(temp, fb, pdt);
  1849. temp[ pdt ] = 0;
  1850. s.m_blob = stackLine;
  1851. }
  1852. else
  1853. {
  1854. pdt = tempSize - 5;
  1855. memcpy(temp, fb, pdt);
  1856. temp[ pdt++ ] = '.';
  1857. temp[ pdt++ ] = '.';
  1858. temp[ pdt++ ] = '.';
  1859. temp[ pdt ] = 0;
  1860. s.m_blob = stackLine;
  1861. }
  1862. int line = 0;
  1863. const char* ns = strchr(stackLine, '(');
  1864. if (ns)
  1865. {
  1866. ++ns;
  1867. line = atoi(ns) - 1; // -1 offset to bridge editor vs display
  1868. }
  1869. s.m_blobLine = line;
  1870. sl.push_back(s);
  1871. }
  1872. }
  1873. }
  1874. else
  1875. {
  1876. // function pointers
  1877. LUAEditor::StackEntry s;
  1878. s.m_blobLine = 0;
  1879. s.m_blob = stackLine;
  1880. sl.push_back(s);
  1881. }
  1882. }
  1883. }
  1884. LUAEditor::LUAStackTrackerMessages::Bus::Broadcast(&LUAEditor::LUAStackTrackerMessages::Bus::Events::StackUpdate, sl);
  1885. }
  1886. void Context::RequestWatchedVariable(const AZStd::string& varName)
  1887. {
  1888. //AZ_TracePrintf(LUAEditorDebugName, "RequestWatchedVariable: name=%s\n", varName.c_str());
  1889. LUAEditorDebuggerMessages::Bus::Broadcast(&LUAEditorDebuggerMessages::Bus::Events::GetValue, varName);
  1890. }
  1891. void Context::OnReceivedValueState(const AZ::ScriptContextDebug::DebugValue& value)
  1892. {
  1893. //AZ_TracePrintf(LUAEditorDebugName, "Received LUA var: name=%s, val=%s, type=0x%x, flags=0x%x\n", value.m_name.c_str(), value.m_value.c_str(), (int)value.m_assetType, (int)value.m_flags);
  1894. LUAEditor::LUAWatchesDebuggerMessages::Bus::Broadcast(&LUAEditor::LUAWatchesDebuggerMessages::Bus::Events::WatchesUpdate, value);
  1895. }
  1896. void Context::OnSetValueResult(const AZStd::string& /*name*/, bool /*success*/)
  1897. {
  1898. //AZ_TracePrintf(LUAEditorDebugName, "SetValue(%s) %s.\n", name.c_str(), success ? "succeeded" : "failed");
  1899. }
  1900. void Context::OnExecutionResumed()
  1901. {
  1902. //AZ_TracePrintf(LUAEditorDebugName, "Context::OnExecutionResumed()\n");
  1903. if (m_pLUAEditorMainWindow)
  1904. {
  1905. m_pLUAEditorMainWindow->MoveProgramCursor("", -1);
  1906. }
  1907. LUAEditor::LUAStackTrackerMessages::Bus::Broadcast(&LUAEditor::LUAStackTrackerMessages::Bus::Events::StackClear);
  1908. LUABreakpointTrackerMessages::Bus::Broadcast(&LUABreakpointTrackerMessages::Bus::Events::BreakpointResume);
  1909. }
  1910. void Context::OnExecuteScriptResult(bool success)
  1911. {
  1912. //AZ_TracePrintf(LUAEditorDebugName, "Context::OnExecutionScriptResult( %d )\n", success);
  1913. LUAEditorMainWindowMessages::Bus::Broadcast(&LUAEditorMainWindowMessages::Bus::Events::OnExecuteScriptResult, success);
  1914. }
  1915. void Context::ResetTargetContexts()
  1916. {
  1917. m_targetContexts.clear();
  1918. m_currentTargetContext = "Default";
  1919. LUAEditor::Context_ControlManagement::Bus::Broadcast(
  1920. &LUAEditor::Context_ControlManagement::Bus::Events::OnTargetContextPrepared, m_currentTargetContext);
  1921. }
  1922. void Context::SetCurrentTargetContext(AZStd::string& contextName)
  1923. {
  1924. //AZ_TracePrintf(LUAEditorDebugName, "Context::SetCurrentTargetContext()\n");
  1925. RequestDetachDebugger();
  1926. // is this a valid context, in our existing list from the target?
  1927. size_t i;
  1928. for (i = 0; i < m_targetContexts.size(); ++i)
  1929. {
  1930. if (contextName == m_targetContexts[i])
  1931. {
  1932. m_currentTargetContext = contextName;
  1933. break;
  1934. }
  1935. }
  1936. if (i >= m_targetContexts.size())
  1937. {
  1938. ResetTargetContexts();
  1939. }
  1940. LUAEditor::Context_ControlManagement::Bus::Broadcast(
  1941. &LUAEditor::Context_ControlManagement::Bus::Events::OnTargetContextPrepared, m_currentTargetContext);
  1942. UpdateReferenceWindow();
  1943. RequestAttachDebugger();
  1944. }
  1945. //////////////////////////////////////////////////////////////////////////
  1946. //Debug Request messages
  1947. //////////////////////////////////////////////////////////////////////////
  1948. void Context::RequestDetachDebugger()
  1949. {
  1950. LUAEditorDebuggerMessages::Bus::Broadcast(&LUAEditorDebuggerMessages::Bus::Events::DetachDebugger);
  1951. }
  1952. void Context::RequestAttachDebugger()
  1953. {
  1954. LUAEditorDebuggerMessages::Bus::Broadcast(&LUAEditorDebuggerMessages::Bus::Events::AttachDebugger, m_currentTargetContext.c_str());
  1955. }
  1956. ReferenceItem::ReferenceItem(const QIcon& icon, const QString& text, size_t id)
  1957. : QStandardItem(icon, text)
  1958. , m_Id(id)
  1959. {
  1960. }
  1961. ReferenceItem::ReferenceItem(const QString& text, size_t id)
  1962. : QStandardItem(text)
  1963. , m_Id(id)
  1964. {
  1965. }
  1966. void LUAEditorContextSavedState::Reflect(AZ::ReflectContext* reflection)
  1967. {
  1968. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection);
  1969. if (serializeContext)
  1970. {
  1971. serializeContext->Class<LUAEditorContextSavedState, AZ::UserSettings >()
  1972. ->Version(1)
  1973. ->Field("m_MainEditorWindowIsVisible", &LUAEditorContextSavedState::m_MainEditorWindowIsVisible)
  1974. ->Field("m_MainEditorWindowIsOpen", &LUAEditorContextSavedState::m_MainEditorWindowIsOpen);
  1975. }
  1976. }
  1977. //////////////////////////////////////////////////////////////////////////
  1978. // ContextFactory
  1979. //////////////////////////////////////////////////////////////////////////
  1980. void Context::Reflect(AZ::ReflectContext* reflection)
  1981. {
  1982. AzToolsFramework::LogPanel::BaseLogPanel::Reflect(reflection);
  1983. AzToolsFramework::QTreeViewWithStateSaving::Reflect(reflection);
  1984. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection);
  1985. if (serializeContext)
  1986. {
  1987. Breakpoint::Reflect(reflection);
  1988. BreakpointSavedState::Reflect(reflection);
  1989. LUAEditorMainWindowSavedState::Reflect(reflection);
  1990. LUAEditorContextSavedState::Reflect(reflection);
  1991. SyntaxStyleSettings::Reflect(reflection);
  1992. serializeContext->Class<Context>()
  1993. ->Version(10)
  1994. ;
  1995. }
  1996. AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(reflection);
  1997. if (behaviorContext)
  1998. {
  1999. behaviorContext->Class<Context>("LUAEditor")->
  2000. Method("SaveLayout", &Context::SaveLayout)->
  2001. Method("LoadLayout", &Context::LoadLayout);
  2002. behaviorContext->Property("luaEditor", BehaviorValueGetter(&s_pLUAEditorScriptPtr), nullptr);
  2003. }
  2004. }
  2005. void Context::AssetCompilationSuccess(const AZStd::string& assetPath)
  2006. {
  2007. if (IsLuaAsset(assetPath))
  2008. {
  2009. AZ_TracePrintf(LUAEditorInfoName, "Compilation Successful - %s\n", assetPath.c_str());
  2010. }
  2011. }
  2012. void Context::AssetCompilationFailed(const AZStd::string& assetPath)
  2013. {
  2014. if (IsLuaAsset(assetPath))
  2015. {
  2016. AZStd::lock_guard<AZStd::mutex> lock(m_failedAssetMessagesMutex);
  2017. m_failedAssets.push(assetPath);
  2018. AZ::SystemTickBus::QueueFunction(&Context::ProcessFailedAssetMessages, this);
  2019. }
  2020. }
  2021. void Context::ProcessFailedAssetMessages()
  2022. {
  2023. AZStd::string currentAsset;
  2024. bool foundAsset = false;
  2025. do
  2026. {
  2027. {
  2028. AZStd::lock_guard<AZStd::mutex> lock(m_failedAssetMessagesMutex);
  2029. if (m_failedAssets.empty())
  2030. {
  2031. foundAsset = false;
  2032. }
  2033. else
  2034. {
  2035. foundAsset = true;
  2036. currentAsset = AZStd::move(m_failedAssets.front());
  2037. m_failedAssets.pop();
  2038. }
  2039. }
  2040. if (foundAsset)
  2041. {
  2042. AZStd::string msg = AZStd::string::format("Compilation Failed! (%s)\n", currentAsset.c_str());
  2043. AZ_Warning(LUAEditorInfoName, false, msg.c_str());
  2044. AZ::Outcome<AzToolsFramework::AssetSystem::JobInfoContainer> jobInfoResult = AZ::Failure();
  2045. AzToolsFramework::AssetSystemJobRequestBus::BroadcastResult(jobInfoResult, &AzToolsFramework::AssetSystemJobRequestBus::Events::GetAssetJobsInfo, currentAsset, false);
  2046. if (jobInfoResult.IsSuccess())
  2047. {
  2048. const AzToolsFramework::AssetSystem::JobInfo &jobInfo = jobInfoResult.GetValue()[0];
  2049. AZ::Outcome<AZStd::string> logResult = AZ::Failure();
  2050. AzToolsFramework::AssetSystemJobRequestBus::BroadcastResult(logResult, &AzToolsFramework::AssetSystemJobRequestBus::Events::GetJobLog, jobInfo.m_jobRunKey);
  2051. if (logResult.IsSuccess())
  2052. {
  2053. // Errors should come in the form of <timestamp> filename.lua:####: errormsg
  2054. std::regex errorRegex(".+\\.lua:(\\d+):(.*)");
  2055. AzToolsFramework::Logging::LogLine::ParseLog(logResult.GetValue().c_str(), logResult.GetValue().size(),
  2056. [this, &currentAsset, &errorRegex](AzToolsFramework::Logging::LogLine& logLine)
  2057. {
  2058. if ((logLine.GetLogType() == AzToolsFramework::Logging::LogLine::TYPE_WARNING) || (logLine.GetLogType() == AzToolsFramework::Logging::LogLine::TYPE_ERROR))
  2059. {
  2060. if (m_pLUAEditorMainWindow)
  2061. {
  2062. m_errorData.push_back(new CompilationErrorData());
  2063. auto errorData = m_errorData.back();
  2064. // Get the full path from the currentAsset
  2065. bool pathFound = false;
  2066. AZStd::string fullPath;
  2067. AzToolsFramework::AssetSystemRequestBus::BroadcastResult(pathFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetFullSourcePathFromRelativeProductPath, currentAsset, errorData->m_filename);
  2068. // Lower this so that it matches the asset_id used by the rest of the lua id when referring to open files
  2069. AZStd::to_lower(errorData->m_filename.begin(), errorData->m_filename.end());
  2070. // Errors should come in the form of <timestamp> filename.lua:####: errormsg
  2071. AZStd::string logString = logLine.ToString();
  2072. // Default the final message to the entire line in case it can't be parsed for line number and actual error
  2073. AZStd::string finalMessage = logString;
  2074. // Try to extract the line number here
  2075. std::smatch match;
  2076. std::string stdLogString = logString.c_str();
  2077. bool matchFound = std::regex_search(stdLogString, match, errorRegex);
  2078. if (matchFound)
  2079. {
  2080. int lineNumber = 0;
  2081. if (AZ::StringFunc::LooksLikeInt(match[1].str().c_str(), &lineNumber))
  2082. {
  2083. errorData->m_lineNumber = lineNumber;
  2084. finalMessage = match[2].str().c_str();
  2085. }
  2086. }
  2087. m_pLUAEditorMainWindow->AddMessageToLog(logLine.GetLogType(), LUAEditorInfoName, finalMessage.c_str(), errorData);
  2088. }
  2089. }
  2090. });
  2091. }
  2092. }
  2093. }
  2094. } while (foundAsset); // keep doing this as long as there's failed assets in the queue
  2095. }
  2096. bool Context::IsLuaAsset(const AZStd::string& assetPath)
  2097. {
  2098. return AZ::StringFunc::Path::IsExtension(assetPath.c_str(), ".lua");
  2099. }
  2100. }