[Scummvm-git-logs] scummvm master -> f596c31c95abfbe0c6e4dd5aebd27910adb6cc31

scemino noreply at scummvm.org
Sat Jun 1 06:11:05 UTC 2024


This automated email contains information about 4 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
be8a1a26a7 DIRECTOR: Fix script navigation during debug
f14f4e32aa DIRECTOR: Add script decompiler for D2 and D3
23c472d3b8 DIRECTOR: Add indentation in script for D2 and D3
f596c31c95 DIRECTOR: Add syntax highlighting in script for D2 and D3


Commit: be8a1a26a72b31e46c39f5bfaeb03bdd36fbb732
    https://github.com/scummvm/scummvm/commit/be8a1a26a72b31e46c39f5bfaeb03bdd36fbb732
Author: scemino (scemino74 at gmail.com)
Date: 2024-06-01T07:51:02+02:00

Commit Message:
DIRECTOR: Fix script navigation during debug

Changed paths:
    engines/director/debugtools.cpp


diff --git a/engines/director/debugtools.cpp b/engines/director/debugtools.cpp
index 424a2ba0982..bd700121c5e 100644
--- a/engines/director/debugtools.cpp
+++ b/engines/director/debugtools.cpp
@@ -379,6 +379,7 @@ typedef struct ImGuiState {
 	struct {
 		uint _lastLinePC = 0;
 		uint _callstackSize = 0;
+		bool _isScriptDirty = false; // indicates whether or not we have to display the script corresponding to the current stackframe
 	} _dbg;
 
 	struct {
@@ -1626,7 +1627,7 @@ private:
 		if (showCurrentStatement) {
 			dl->AddQuadFilled(ImVec2(pos.x, pos.y + 4.f), ImVec2(pos.x + 9.f, pos.y + 4.f), ImVec2(pos.x + 9.f, pos.y + 10.f), ImVec2(pos.x, pos.y + 10.f), ImColor(_state->_colors._current_statement));
 			dl->AddTriangleFilled(ImVec2(pos.x + 8.f, pos.y), ImVec2(pos.x + 14.f, pos.y + 7.f), ImVec2(pos.x + 8.f, pos.y + 14.f), ImColor(_state->_colors._current_statement));
-			if (!ImGui::IsItemVisible()) {
+			if (_state->_dbg._isScriptDirty && !ImGui::IsItemVisible()) {
 				ImGui::SetScrollHereY(0.5f);
 			}
 			dl->AddRectFilled(ImVec2(pos.x + 16.f, pos.y), ImVec2(pos.x + width, pos.y + 16.f), ImColor(IM_COL32(0xFF, 0xFF, 0x00, 0x20)), 0.4f);
@@ -1727,6 +1728,36 @@ static bool stepOutShouldPause() {
 	return false;
 }
 
+static void dgbStop() {
+	g_lingo->_exec._state = kPause;
+	g_lingo->_exec._shouldPause = nullptr;
+	_state->_dbg._isScriptDirty = true;
+}
+
+static void dbgStepOver() {
+	g_lingo->_exec._state = kRunning;
+	_state->_dbg._lastLinePC = getLineFromPC();
+	_state->_dbg._callstackSize = g_lingo->_state->callstack.size();
+	g_lingo->_exec._shouldPause = stepOverShouldPauseDebugger;
+	_state->_dbg._isScriptDirty = true;
+}
+
+static void dbgStepInto() {
+	g_lingo->_exec._state = kRunning;
+	_state->_dbg._lastLinePC = getLineFromPC();
+	_state->_dbg._callstackSize = g_lingo->_state->callstack.size();
+	g_lingo->_exec._shouldPause = stepInShouldPauseDebugger;
+	_state->_dbg._isScriptDirty = true;
+}
+
+static void dbgStepOut() {
+	g_lingo->_exec._state = kRunning;
+	_state->_dbg._lastLinePC = getLineFromPC();
+	_state->_dbg._callstackSize = g_lingo->_state->callstack.size();
+	g_lingo->_exec._shouldPause = stepOutShouldPause;
+	_state->_dbg._isScriptDirty = true;
+}
+
 static void showControlPanel() {
 	if (!_state->_w.controlPanel)
 		return;
@@ -1802,6 +1833,7 @@ static void showControlPanel() {
 				score->_playState = kPlayPaused;
 				g_lingo->_exec._state = kPause;
 				g_lingo->_exec._shouldPause = nullptr;
+				_state->_dbg._isScriptDirty = true;
 			}
 
 			if (ImGui::IsItemHovered())
@@ -1878,10 +1910,11 @@ static void showControlPanel() {
 
 			if (ImGui::IsItemClicked(0)) {
 				score->_playState = kPlayStarted;
-				g_lingo->_exec._state = kRunning;
-				_state->_dbg._lastLinePC = getLineFromPC();
-				_state->_dbg._callstackSize = g_lingo->_state->callstack.size();
-				g_lingo->_exec._shouldPause = stepOverShouldPauseDebugger;
+				if (g_lingo->_exec._state == kRunning) {
+					dgbStop();
+				} else {
+					dbgStepOver();
+				}
 			}
 
 			if (ImGui::IsItemHovered())
@@ -1903,10 +1936,11 @@ static void showControlPanel() {
 
 			if (ImGui::IsItemClicked(0)) {
 				score->_playState = kPlayStarted;
-				g_lingo->_exec._state = kRunning;
-				_state->_dbg._lastLinePC = getLineFromPC();
-				_state->_dbg._callstackSize = g_lingo->_state->callstack.size();
-				g_lingo->_exec._shouldPause = stepInShouldPauseDebugger;
+				if (g_lingo->_exec._state == kRunning) {
+					dgbStop();
+				} else {
+					dbgStepInto();
+				}
 			}
 
 			if (ImGui::IsItemHovered())
@@ -1927,10 +1961,11 @@ static void showControlPanel() {
 
 			if (ImGui::IsItemClicked(0)) {
 				score->_playState = kPlayStarted;
-				g_lingo->_exec._state = kRunning;
-				_state->_dbg._lastLinePC = getLineFromPC();
-				_state->_dbg._callstackSize = g_lingo->_state->callstack.size();
-				g_lingo->_exec._shouldPause = stepOutShouldPause;
+				if (g_lingo->_exec._state == kRunning) {
+					dgbStop();
+				} else {
+					dbgStepOut();
+				}
 			}
 
 			if (ImGui::IsItemHovered())
@@ -2603,6 +2638,7 @@ static void renderScript(ImGuiScript &script, bool showByteCode) {
 
 	RenderScriptVisitor visitor(script, showByteCode);
 	script.root->accept(visitor);
+	_state->_dbg._isScriptDirty = false;
 }
 
 static bool showScriptCast(CastMemberID &id) {
@@ -2654,7 +2690,7 @@ static void displayScriptCasts() {
 }
 
 static void updateCurrentScript() {
-	if (g_lingo->_exec._state != kPause)
+	if ((g_lingo->_exec._state != kPause) || !_state->_dbg._isScriptDirty)
 		return;
 
 	Common::Array<CFrame *> &callstack = g_lingo->_state->callstack;


Commit: f14f4e32aa43e0e9c963a613e38ced3d11df8e6a
    https://github.com/scummvm/scummvm/commit/f14f4e32aa43e0e9c963a613e38ced3d11df8e6a
Author: scemino (scemino74 at gmail.com)
Date: 2024-06-01T07:51:02+02:00

Commit Message:
DIRECTOR: Add script decompiler for D2 and D3

Changed paths:
    engines/director/debugtools.cpp
    engines/director/lingo/lingo-ast.h
    engines/director/lingo/lingo-codegen.cpp
    engines/director/lingo/lingo-codegen.h
    engines/director/lingo/lingo-gr.cpp
    engines/director/lingo/lingo-gr.y
    engines/director/lingo/lingo-object.cpp
    engines/director/lingo/lingo-object.h


diff --git a/engines/director/debugtools.cpp b/engines/director/debugtools.cpp
index bd700121c5e..a66a9a21606 100644
--- a/engines/director/debugtools.cpp
+++ b/engines/director/debugtools.cpp
@@ -32,7 +32,10 @@
 
 #include "director/director.h"
 #include "director/lingo/lingo.h"
+#include "director/lingo/lingo-ast.h"
+#include "director/lingo/lingo-code.h"
 #include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-the.h"
 #include "director/lingo/lingodec/ast.h"
 #include "director/lingo/lingodec/codewritervisitor.h"
 #include "director/lingo/lingodec/context.h"
@@ -90,7 +93,7 @@ typedef struct ImGuiScript {
 	Common::SharedPtr<LingoDec::HandlerNode> root;
 	Common::Array<LingoDec::Bytecode> bytecodeArray;
 	Common::Array<uint> startOffsets;
-
+	Common::SharedPtr<Node> oldAst;
 
 	bool operator==(const ImGuiScript &c) const {
 		return moviePath == c.moviePath && score == c.score && id == c.id && handlerId == c.handlerId;
@@ -443,6 +446,9 @@ const LingoDec::Handler *getHandler(const Cast *cast, CastMemberID id, const Com
 	const ScriptContext *ctx = cast->_lingoArchive->findScriptContext(id.member);
 	if (!ctx || !ctx->_functionHandlers.contains(handlerId))
 		return nullptr;
+	// for the moment it's happening with Director version < 4
+	if (!cast->_lingodec)
+		return nullptr;
 	for (auto p : cast->_lingodec->scripts) {
 		if ((p.second->castID & 0xFFFF) != id.member)
 			continue;
@@ -467,14 +473,24 @@ const LingoDec::Handler *getHandler(CastMemberID id, const Common::String &handl
 	return getHandler(movie->getSharedCast(), id, handlerId);
 }
 
-ImGuiScript toImGuiScript(CastMemberID id, const Common::String &handlerId) {
+ImGuiScript toImGuiScript(ScriptType scriptType, CastMemberID id, const Common::String &handlerId) {
 	ImGuiScript result;
 	result.id = id;
 	result.handlerId = handlerId;
+	result.type = scriptType;
 
 	const LingoDec::Handler *handler = getHandler(id, handlerId);
-	if (!handler)
+	if (!handler) {
+		const ScriptContext *ctx;
+		if (id.castLib == SHARED_CAST_LIB) {
+			ctx = g_director->getCurrentMovie()->getSharedCast()->_lingoArchive->getScriptContext(scriptType, id.member);
+		} else {
+			ctx = g_director->getCurrentMovie()->getScriptContext(scriptType, id);
+		}
+		if (!ctx) return result;
+		result.oldAst = ctx->_assemblyAST;
 		return result;
+	}
 
 	result.bytecodeArray = handler->bytecodeArray;
 	result.root = handler->ast.root;
@@ -503,9 +519,583 @@ static Director::Breakpoint *getBreakpoint(const Common::String &handlerName, ui
 	return nullptr;
 }
 
+class RenderOldScriptVisitor : public NodeVisitor {
+private:
+	ImGuiScript &_script;
+
+public:
+	explicit RenderOldScriptVisitor(ImGuiScript &script) : _script(script) {}
+
+	virtual bool visitHandlerNode(HandlerNode *node) {
+		ImGui::Text("on %s", node->name->c_str());
+		if(!node->args->empty()) {
+			ImGui::SameLine();
+			ImGui::Text(" ");
+			ImGui::SameLine();
+			for (uint i = 0; i < node->args->size(); i++) {
+				Common::String *arg = (*node->args)[i];
+				ImGui::Text("%s", arg->c_str());
+				ImGui::SameLine();
+				if (i != (node->args->size() - 1)) {
+					ImGui::Text(", ");
+					ImGui::SameLine();
+				}
+			}
+			ImGui::NewLine();
+		}
+		for (uint i = 0; i < node->stmts->size(); i++) {
+			Node *stmt = (*node->stmts)[i];
+			stmt->accept(this);
+			ImGui::NewLine();
+		}
+		ImGui::Text("end");
+		return true;
+	}
+
+	virtual bool visitScriptNode(ScriptNode *node){
+		ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2());
+		for(Node* child : *node->children) {
+			if(child->type == kHandlerNode && *((HandlerNode*)child)->name != _script.handlerId) continue;
+			child->accept(this);
+		}
+		ImGui::PopStyleVar();
+		return true;
+	}
+	virtual bool visitFactoryNode(FactoryNode *node){
+		ImGui::Text("factory %s", node->name->c_str());
+		ImGui::SameLine();
+		for (uint i = 0; i < node->methods->size(); i++) {
+			Node *method = (*node->methods)[i];
+			method->accept(this);
+			ImGui::NewLine();
+		}
+		return true;
+	}
+	virtual bool visitCmdNode(CmdNode *node) {
+		ImGui::Text("%s ", node->name->c_str());
+		ImGui::SameLine();
+		if(*node->name == "go") {
+			ImGui::Text("to ");
+			ImGui::SameLine();
+		}
+		for (uint i = 0; i < node->args->size(); i++) {
+			Node *arg = (*node->args)[i];
+			if ((i != 0) || (node->args->size() < 2) || ((*node->args)[1]->type != kMovieNode)) {
+				arg->accept(this);
+				ImGui::SameLine();
+				if (i != (node->args->size() - 1)) {
+					ImGui::Text(", ");
+					ImGui::SameLine();
+				}
+			}
+		}
+		return true;
+	}
+	virtual bool visitPutIntoNode(PutIntoNode *node){
+		ImGui::Text("put ");
+		ImGui::SameLine();
+		node->val->accept(this);
+		ImGui::Text(" into ");
+		ImGui::SameLine();
+		node->var->accept(this);
+		return true;
+	}
+	virtual bool visitPutAfterNode(PutAfterNode *node) {
+		ImGui::Text("put ");
+		ImGui::SameLine();
+		node->val->accept(this);
+		ImGui::Text(" after ");
+		ImGui::SameLine();
+		node->var->accept(this);
+		return true;
+	}
+	virtual bool visitPutBeforeNode(PutBeforeNode *node){
+		ImGui::Text("put ");
+		ImGui::SameLine();
+		node->val->accept(this);
+		ImGui::Text(" before ");
+		ImGui::SameLine();
+		node->var->accept(this);
+		return true;
+	}
+	virtual bool visitSetNode(SetNode *node){
+		ImGui::Text("set ");
+		ImGui::SameLine();
+		node->val->accept(this);
+		ImGui::Text(" to ");
+		ImGui::SameLine();
+		node->var->accept(this);
+		return true;
+	}
+
+	void displayDefineVar(const char *name, IDList *names) {
+		ImGui::Text("%s ", name);
+		ImGui::SameLine();
+		for (uint i = 0; i < names->size(); i++) {
+			Common::String *arg = (*names)[i];
+			ImGui::Text("%s", arg->c_str());
+			ImGui::SameLine();
+			if (i != (names->size() - 1)) {
+				ImGui::Text(" ");
+				ImGui::SameLine();
+			}
+		}
+	}
+
+	virtual bool visitGlobalNode(GlobalNode *node){
+		displayDefineVar("global ", node->names);
+		return true;
+	}
+	virtual bool visitPropertyNode(PropertyNode *node){
+		displayDefineVar("property ", node->names);
+		return true;
+	}
+	virtual bool visitInstanceNode(InstanceNode *node){
+		displayDefineVar("instance ", node->names);
+		return true;
+	}
+	virtual bool visitIfStmtNode(IfStmtNode *node){
+		ImGui::Text("if ");
+		ImGui::SameLine();
+		node->cond->accept(this);
+		ImGui::Text(" then ");
+		if(node->stmts->size() == 1) {
+			ImGui::SameLine();
+			(*node->stmts)[0]->accept(this);
+		} else {
+			for (uint i = 0; i < node->stmts->size(); i++) {
+				Node *stmt = (*node->stmts)[i];
+				stmt->accept(this);
+				ImGui::NewLine();
+			}
+			ImGui::Text("endif");
+			ImGui::SameLine();
+		}
+		return true;
+	}
+	virtual bool visitIfElseStmtNode(IfElseStmtNode *node){
+		ImGui::Text("if ");
+		ImGui::SameLine();
+		node->cond->accept(this);
+		ImGui::Text(" then ");
+		if(node->stmts1->size() == 1) {
+			ImGui::SameLine();
+			(*node->stmts1)[0]->accept(this);
+			ImGui::Text(" ");
+			ImGui::SameLine();
+		} else {
+			for (uint i = 0; i < node->stmts1->size(); i++) {
+				Node *stmt = (*node->stmts1)[i];
+				stmt->accept(this);
+				ImGui::NewLine();
+			}
+		}
+		ImGui::Text("else ");
+		if(node->stmts2->size() == 1) {
+			ImGui::SameLine();
+			(*node->stmts2)[0]->accept(this);
+		} else {
+			for (uint i = 0; i < node->stmts2->size(); i++) {
+				Node *stmt = (*node->stmts2)[i];
+				stmt->accept(this);
+				ImGui::NewLine();
+			}
+			ImGui::Text("endif");
+			ImGui::SameLine();
+		}
+		return true;
+	}
+	virtual bool visitRepeatWhileNode(RepeatWhileNode *node){
+		ImGui::Text("repeat while ");
+		ImGui::SameLine();
+		node->cond->accept(this);
+		ImGui::NewLine();
+		for (uint i = 0; i < node->stmts->size(); i++) {
+			Node *stmt = (*node->stmts)[i];
+			stmt->accept(this);
+			ImGui::NewLine();
+		}
+		ImGui::Text("endrepeat");
+		return true;
+	}
+	virtual bool visitRepeatWithToNode(RepeatWithToNode *node){
+		ImGui::Text("repeat with ");
+		ImGui::SameLine();
+		ImGui::Text("%s = ", node->var->c_str());
+		ImGui::SameLine();
+		node->start->accept(this);
+		ImGui::Text(" %s ", node->down ? "down to" : "to");
+		node->end->accept(this);
+		ImGui::NewLine();
+		for (uint i = 0; i < node->stmts->size(); i++) {
+			Node *stmt = (*node->stmts)[i];
+			stmt->accept(this);
+			ImGui::NewLine();
+		}
+		ImGui::Text("endrepeat");
+		return true;
+	}
+	virtual bool visitRepeatWithInNode(RepeatWithInNode *node){
+		ImGui::Text("repeat with ");
+		ImGui::SameLine();
+		ImGui::Text("%s in ", node->var->c_str());
+		ImGui::SameLine();
+		node->list->accept(this);
+		ImGui::NewLine();
+		for (uint i = 0; i < node->stmts->size(); i++) {
+			Node *stmt = (*node->stmts)[i];
+			stmt->accept(this);
+			ImGui::NewLine();
+		}
+		ImGui::Text("endrepeat");
+		return true;
+	}
+	virtual bool visitNextRepeatNode(NextRepeatNode *node){
+		ImGui::Text("next repeat");
+		return true;
+	}
+	virtual bool visitExitRepeatNode(ExitRepeatNode *node){
+		ImGui::Text("exit repeat");
+		return true;
+	}
+	virtual bool visitExitNode(ExitNode *node){
+		ImGui::Text("exit");
+		return true;
+	}
+	virtual bool visitReturnNode(ReturnNode *node){
+		ImGui::Text("return");
+		if(node->expr) {
+			ImGui::Text(" ");
+			ImGui::SameLine();
+			node->expr->accept(this);
+			ImGui::NewLine();
+		}
+		return true;
+	}
+	virtual bool visitTellNode(TellNode *node){
+		ImGui::Text("tell ");
+		node->target->accept(this);
+		if(node->stmts->size() == 1) {
+			ImGui::SameLine();
+			ImGui::Text(" to ");
+			ImGui::SameLine();
+			(*node->stmts)[0]->accept(this);
+		} else {
+			for (uint i = 0; i < node->stmts->size(); i++) {
+				Node *stmt = (*node->stmts)[i];
+				stmt->accept(this);
+				ImGui::NewLine();
+			}
+			ImGui::Text("endtell");
+		}
+		return true;
+	}
+	virtual bool visitWhenNode(WhenNode *node){
+		ImGui::Text("when %s then %s", node->event->c_str(), node->code->c_str());
+		ImGui::SameLine();
+		return true;
+	}
+	virtual bool visitDeleteNode(DeleteNode *node){
+		ImGui::Text("delete ");
+		ImGui::SameLine();
+		node->chunk->accept(this);
+		return true;
+	}
+	virtual bool visitHiliteNode(HiliteNode *node){
+		ImGui::Text("hilite ");
+		ImGui::SameLine();
+		node->chunk->accept(this);
+		return true;
+	}
+	virtual bool visitAssertErrorNode(AssertErrorNode *node){
+		ImGui::Text("scummvmAssertError ");
+		ImGui::SameLine();
+		node->stmt->accept(this);
+		return true;
+	}
+	virtual bool visitIntNode(IntNode *node){
+		ImGui::Text("%d", node->val);
+		ImGui::SameLine();
+		return true;
+	}
+	virtual bool visitFloatNode(FloatNode *node){
+		ImGui::Text("%g", node->val);
+		ImGui::SameLine();
+		return true;
+	}
+	virtual bool visitSymbolNode(SymbolNode *node){
+		ImGui::Text("%s", node->val->c_str());
+		ImGui::SameLine();
+		return true;
+	}
+	virtual bool visitStringNode(StringNode *node){
+		ImGui::Text("\"%s\"", node->val->c_str());
+		ImGui::SameLine();
+		return true;
+	}
+	virtual bool visitListNode(ListNode *node){
+		ImGui::Text("[");
+		ImGui::SameLine();
+		for (uint i = 0; i < node->items->size(); i++) {
+			Node *prop = (*node->items)[i];
+			prop->accept(this);
+			if (i != (node->items->size() - 1)) {
+				ImGui::Text(",");
+				ImGui::SameLine();
+			}
+		}
+		ImGui::Text("]");
+		ImGui::SameLine();
+		return true;
+	}
+	virtual bool visitPropListNode(PropListNode *node){
+		ImGui::Text("[");
+		ImGui::SameLine();
+		if(node->items->empty()) {
+			ImGui::Text(":");
+			ImGui::SameLine();
+		} else {
+			for (uint i = 0; i < node->items->size(); i++) {
+				Node *prop = (*node->items)[i];
+				prop->accept(this);
+				if (i != (node->items->size() - 1)) {
+					ImGui::Text(",");
+					ImGui::SameLine();
+				}
+			}
+		}
+		ImGui::Text("]");
+		ImGui::SameLine();
+		return true;
+	}
+	virtual bool visitPropPairNode(PropPairNode *node){
+		node->key->accept(this);
+		ImGui::Text(":");
+		ImGui::SameLine();
+		node->val->accept(this);
+		return true;
+	}
+	virtual bool visitFuncNode(FuncNode *node){
+		ImGui::Text("%s(", node->name->c_str());
+		ImGui::SameLine();
+		for (uint i = 0; i < node->args->size(); i++) {
+			Node *arg = (*node->args)[i];
+			arg->accept(this);
+			if (i != (node->args->size() - 1)) {
+				ImGui::Text(",");
+				ImGui::SameLine();
+			}
+		}
+		ImGui::Text(")");
+		ImGui::SameLine();
+		return true;
+	}
+	virtual bool visitVarNode(VarNode *node){
+		ImGui::Text("%s", node->name->c_str());
+		ImGui::SameLine();
+		return true;
+	}
+	virtual bool visitParensNode(ParensNode *node){
+		ImGui::Text("(");
+		ImGui::SameLine();
+		node->expr->accept(this);
+		ImGui::Text(")");
+		ImGui::SameLine();
+		return true;
+	}
+	virtual bool visitUnaryOpNode(UnaryOpNode *node){
+		char op = '?';
+		if(node->op == LC::c_negate) {
+			op = '-';
+		} else if(node->op == LC::c_not) {
+			op = '!';
+		}
+		ImGui::Text("%c", op);
+		ImGui::SameLine();
+		node->arg->accept(this);
+		return true;
+	}
+	virtual bool visitBinaryOpNode(BinaryOpNode *node){
+		node->a->accept(this);
+		static struct {
+			inst op;
+			const char *name;
+		} ops[] = {
+			{LC::c_add, "+"},
+			{LC::c_sub, "-"},
+			{LC::c_mul, "*"},
+			{LC::c_div, "/"},
+			{LC::c_mod, "mod"},
+			{LC::c_gt, ">"},
+			{LC::c_lt, "<"},
+			{LC::c_eq, "="},
+			{LC::c_neq, "<>"},
+			{LC::c_ge, ">="},
+			{LC::c_le, "<="},
+			{LC::c_and, "and"},
+			{LC::c_or, "or"},
+			{LC::c_ampersand, "&"},
+			{LC::c_concat, "&&"},
+			{LC::c_contains, "contains"},
+			{LC::c_starts, "starts"},
+		};
+		for (auto &op : ops) {
+			if (op.op == node->op) {
+				ImGui::Text(" %s ", op.name);
+				ImGui::SameLine();
+				break;
+			}
+		}
+		node->b->accept(this);
+		return true;
+	 }
+	virtual bool visitFrameNode(FrameNode *node){
+		ImGui::Text("frame ");
+		ImGui::SameLine();
+		node->arg->accept(this);
+		return true;
+	}
+	virtual bool visitMovieNode(MovieNode *node){
+		ImGui::Text("movie ");
+		ImGui::SameLine();
+		node->arg->accept(this);
+		return true;
+	}
+	virtual bool visitIntersectsNode(IntersectsNode *node){
+		ImGui::Text("sprite ");
+		ImGui::SameLine();
+		node->sprite1->accept(this);
+		ImGui::Text("intersects ");
+		node->sprite2->accept(this);
+		return true;
+	}
+	virtual bool visitWithinNode(WithinNode *node){
+		ImGui::Text("sprite ");
+		ImGui::SameLine();
+		node->sprite1->accept(this);
+		ImGui::Text("within ");
+		node->sprite2->accept(this);
+		return true;
+	}
+	virtual bool visitTheNode(TheNode *node){
+		ImGui::Text("the %s", node->prop->c_str());
+		ImGui::SameLine();
+		return true;
+	}
+	virtual bool visitTheOfNode(TheOfNode *node){
+		ImGui::Text("the %s of ", node->prop->c_str());
+		ImGui::SameLine();
+		node->obj->accept(this);
+		return true;
+	}
+	virtual bool visitTheNumberOfNode(TheNumberOfNode *node){
+		ImGui::Text("the number of ");
+		ImGui::SameLine();
+		node->arg->accept(this);
+		return true;
+	}
+	virtual bool visitTheLastNode(TheLastNode *node){
+		// TODO: change the node to know if it's 'in' or 'of'
+		ImGui::Text("the last %s in/of ", toString(node->type).c_str());
+		ImGui::SameLine();
+		node->arg->accept(this);
+		return true;
+	}
+	virtual bool visitTheDateTimeNode(TheDateTimeNode *node){
+		const char* key1 = "";
+		switch(node->field) {
+			case kTheAbbr:
+			key1 = "abbreviated";
+			break;
+			case kTheLong:
+			key1 = "long";
+			break;
+			case kTheShort:
+			key1 = "short";
+			break;
+		}
+		const char *key2 = node->entity == kTheDate ? "date" : "time";
+		ImGui::Text("the %s %s", key1, key2);
+		ImGui::SameLine();
+		return true;
+	}
+	virtual bool visitMenuNode(MenuNode *node){
+		ImGui::Text("menu ");
+		ImGui::SameLine();
+		node->arg->accept(this);
+		return true;
+	}
+	virtual bool visitMenuItemNode(MenuItemNode *node){
+		ImGui::Text("menuitem ");
+		ImGui::SameLine();
+		node->arg1->accept(this);
+		ImGui::Text("of menu ");
+		ImGui::SameLine();
+		node->arg2->accept(this);
+		return true;
+	}
+	virtual bool visitSoundNode(SoundNode *node){
+		ImGui::Text("sound ");
+		ImGui::SameLine();
+		node->arg->accept(this);
+		return true;
+	}
+	virtual bool visitSpriteNode(SpriteNode *node){
+		ImGui::Text("sprite ");
+		ImGui::SameLine();
+		node->arg->accept(this);
+		return true;
+	}
+	virtual bool visitChunkExprNode(ChunkExprNode *node){
+		const char* key1 = "";
+		switch(node->type) {
+			case kChunkChar:
+			key1 = "char";
+			break;
+			case kChunkWord:
+			key1 = "word";
+			break;
+			case kChunkItem:
+			key1 = "item";
+			break;
+			case kChunkLine:
+			key1 = "line";
+			break;
+		}
+		ImGui::Text("%s", key1);
+		ImGui::SameLine();
+		node->start->accept(this);
+		if(node->end) {
+			ImGui::Text(" to ");
+			ImGui::SameLine();
+			node->end->accept(this);
+		}
+		ImGui::Text(" of ");
+		ImGui::SameLine();
+		node->src->accept(this);
+		return true;
+	}
+
+private:
+	static Common::String toString(ChunkType chunkType) {
+		// TODO: this method could be used in ChunkExprNode
+		switch(chunkType) {
+			case kChunkChar:
+			return "char";
+			case kChunkWord:
+			return "word";
+			case kChunkItem:
+			return "item";
+			case kChunkLine:
+			return "line";
+		}
+		return "<unknown>";
+	}
+};
+
 class RenderScriptVisitor : public LingoDec::NodeVisitor {
 public:
-	explicit RenderScriptVisitor(ImGuiScript &script, bool showByteCode) : _script(script), _showByteCode(showByteCode) {
+	RenderScriptVisitor(ImGuiScript &script, bool showByteCode) : _script(script), _showByteCode(showByteCode) {
 		Common::Array<CFrame *> &callstack = g_lingo->_state->callstack;
 		if (!callstack.empty()) {
 			CFrame *head = callstack[callstack.size() - 1];
@@ -587,7 +1177,7 @@ public:
 			ImGui::EndTooltip();
 		}
 		if (!g_lingo->_builtinCmds.contains(node.name) && ImGui::IsItemClicked()) {
-			ImGuiScript script = toImGuiScript(CastMemberID(obj, _script.id.castLib), node.name);
+			ImGuiScript script = toImGuiScript(_script.type, CastMemberID(obj, _script.id.castLib), node.name);
 			script.moviePath = _script.moviePath;
 			script.handlerName = node.name;
 			setScriptToDisplay(script);
@@ -2634,7 +3224,14 @@ static void renderCastScript(Symbol &sym) {
 }
 
 static void renderScript(ImGuiScript &script, bool showByteCode) {
-	if (!script.root) return;
+	if (script.oldAst) {
+		RenderOldScriptVisitor oldVisitor(script);
+		script.oldAst->accept(&oldVisitor);
+		_state->_dbg._isScriptDirty = false;
+		return;
+	}
+
+	if(!script.root) return;
 
 	RenderScriptVisitor visitor(script, showByteCode);
 	script.root->accept(visitor);
@@ -2702,7 +3299,7 @@ static void updateCurrentScript() {
 	Director::Movie *movie = g_director->getCurrentMovie();
 	ScriptContext *scriptContext = head->sp.ctx;
 	int castLibID = movie->getCast()->_castLibID;
-	ImGuiScript script = toImGuiScript(CastMemberID(head->sp.ctx->_id, castLibID), *head->sp.name);
+	ImGuiScript script = toImGuiScript(scriptContext->_scriptType, CastMemberID(head->sp.ctx->_id, castLibID), *head->sp.name);
 	script.byteOffsets = scriptContext->_functionByteOffsets[script.handlerId];
 	script.moviePath = movie->getArchive()->getPathName().toString();
 	script.handlerName = head->sp.ctx->_id ? Common::String::format("%d:%s", head->sp.ctx->_id, script.handlerId.c_str()) : script.handlerId;
@@ -2819,7 +3416,7 @@ static void showFuncList() {
 							ImGui::TableNextColumn();
 							if (ImGui::Selectable(function.c_str())) {
 								CastMemberID memberID(scriptContext._key, cast._key);
-								ImGuiScript script = toImGuiScript(memberID, functionHandler._key);
+								ImGuiScript script = toImGuiScript(scriptContext._value->_scriptType, memberID, functionHandler._key);
 								script.byteOffsets = scriptContext._value->_functionByteOffsets[script.handlerId];
 								script.moviePath = movie->getArchive()->getPathName().toString();
 								script.handlerName = getHandlerName(functionHandler._value);
@@ -2857,7 +3454,7 @@ static void showFuncList() {
 							ImGui::TableNextColumn();
 							if (ImGui::Selectable(function.c_str())) {
 								CastMemberID memberID(scriptContext._key, SHARED_CAST_LIB);
-								ImGuiScript script = toImGuiScript(memberID, functionHandler._key);
+								ImGuiScript script = toImGuiScript(scriptContext._value->_scriptType, memberID, functionHandler._key);
 								script.byteOffsets = scriptContext._value->_functionByteOffsets[script.handlerId];
 								script.moviePath = movie->getArchive()->getPathName().toString();
 								script.handlerName = getHandlerName(functionHandler._value);
diff --git a/engines/director/lingo/lingo-ast.h b/engines/director/lingo/lingo-ast.h
index df828a4972a..c4d7ad48a9b 100644
--- a/engines/director/lingo/lingo-ast.h
+++ b/engines/director/lingo/lingo-ast.h
@@ -216,9 +216,7 @@ public:
 };
 
 #define VISITNODE(nodeName) \
-		storeStart(); \
 		bool res = visitor->visit ## nodeName(this); \
-		storeEnd(); \
 		return res
 
 /* Node */
diff --git a/engines/director/lingo/lingo-codegen.cpp b/engines/director/lingo/lingo-codegen.cpp
index fb2c3f575d9..63f86e49b98 100644
--- a/engines/director/lingo/lingo-codegen.cpp
+++ b/engines/director/lingo/lingo-codegen.cpp
@@ -90,13 +90,16 @@ namespace Director {
 			return false; \
 	}
 
-void  Node::storeStart() {
-	startOffset = g_lingo->_compiler->_currentAssembly->size() - 1;
-}
+class NodeStore {
+public:
+	explicit NodeStore(Node *node) : _node(node) {
+		_node->startOffset = g_lingo->_compiler->_currentAssembly->size() - 1;
+	}
+	~NodeStore() { _node->endOffset = g_lingo->_compiler->_currentAssembly->size() - 1; }
 
-void Node::storeEnd() {
-	endOffset = g_lingo->_compiler->_currentAssembly->size() - 1;
-}
+private:
+	Node *_node = nullptr;
+};
 
 LingoCompiler::LingoCompiler() {
 	_assemblyAST = nullptr;
@@ -129,6 +132,7 @@ ScriptContext *LingoCompiler::compileLingo(const Common::U32String &code, LingoA
 	_assemblyArchive = archive;
 	_assemblyAST = nullptr;
 	_assemblyId = id.member;
+	warning("%s: %s", scriptName.c_str(), code.encode().c_str());
 	ScriptContext *mainContext = _assemblyContext = new ScriptContext(scriptName, type, _assemblyId);
 	_currentAssembly = new ScriptData;
 
@@ -178,7 +182,6 @@ ScriptContext *LingoCompiler::compileLingo(const Common::U32String &code, LingoA
 		delete _assemblyContext;
 		delete _currentAssembly;
 		delete _methodVars;
-		delete _assemblyAST;
 		_assemblyId = -1;
 		return nullptr;
 	}
@@ -260,7 +263,7 @@ ScriptContext *LingoCompiler::compileLingo(const Common::U32String &code, LingoA
 	if (debugChannelSet(-1, kDebugImGui)) {
 		_assemblyContext->_assemblyAST = _assemblyAST;
 	} else {
-		delete _assemblyAST;
+		_assemblyAST.reset();
 	}
 	_assemblyAST = nullptr;
 	_assemblyContext = nullptr;
@@ -448,6 +451,7 @@ void LingoCompiler::updateLoopJumps(uint nextTargetPos, uint exitTargetPos) {
 /* ScriptNode */
 
 bool LingoCompiler::visitScriptNode(ScriptNode *node) {
+	NodeStore store(node);
 	COMPILE_LIST(node->children);
 	return true;
 }
@@ -455,6 +459,7 @@ bool LingoCompiler::visitScriptNode(ScriptNode *node) {
 /* FactoryNode */
 
 bool LingoCompiler::visitFactoryNode(FactoryNode *node) {
+	NodeStore store(node);
 	_inFactory = true;
 	ScriptContext *mainContext = _assemblyContext;
 	_assemblyContext = new ScriptContext(*node->name, mainContext->_scriptType, mainContext->_id);
@@ -470,6 +475,7 @@ bool LingoCompiler::visitFactoryNode(FactoryNode *node) {
 /* HandlerNode */
 
 bool LingoCompiler::visitHandlerNode(HandlerNode *node) {
+	NodeStore store(node);
 	_indef = true;
 	ScriptData *mainAssembly = _currentAssembly;
 	_currentAssembly = new ScriptData;
@@ -535,6 +541,7 @@ bool LingoCompiler::visitHandlerNode(HandlerNode *node) {
 /* CmdNode */
 
 bool LingoCompiler::visitCmdNode(CmdNode *node) {
+	NodeStore store(node);
 	uint numargs = node->args->size();
 
 	if (node->name->equalsIgnoreCase("go") && numargs == 1 && (*node->args)[0]->type == kVarNode){
@@ -611,6 +618,7 @@ bool LingoCompiler::visitCmdNode(CmdNode *node) {
 /* PutIntoNode */
 
 bool LingoCompiler::visitPutIntoNode(PutIntoNode *node) {
+	NodeStore store(node);
 	if (node->var->type == kVarNode) {
 		registerMethodVar(*static_cast<VarNode *>(node->var)->name);
 	}
@@ -623,6 +631,7 @@ bool LingoCompiler::visitPutIntoNode(PutIntoNode *node) {
 /* PutAfterNode */
 
 bool LingoCompiler::visitPutAfterNode(PutAfterNode *node) {
+	NodeStore store(node);
 	if (node->var->type == kVarNode) {
 		registerMethodVar(*static_cast<VarNode *>(node->var)->name);
 	}
@@ -635,6 +644,7 @@ bool LingoCompiler::visitPutAfterNode(PutAfterNode *node) {
 /* PutBeforeNode */
 
 bool LingoCompiler::visitPutBeforeNode(PutBeforeNode *node) {
+	NodeStore store(node);
 	if (node->var->type == kVarNode) {
 		registerMethodVar(*static_cast<VarNode *>(node->var)->name);
 	}
@@ -657,6 +667,7 @@ int LingoCompiler::getTheFieldID(int entity, const Common::String &field, bool s
 }
 
 bool LingoCompiler::visitSetNode(SetNode *node) {
+	NodeStore store(node);
 	if (node->var->type == kTheNode) {
 		TheNode *the = static_cast<TheNode *>(node->var);
 		if (g_lingo->_theEntities.contains(*the->prop) && !g_lingo->_theEntities[*the->prop]->hasId) {
@@ -820,6 +831,7 @@ bool LingoCompiler::visitSetNode(SetNode *node) {
 /* GlobalNode */
 
 bool LingoCompiler::visitGlobalNode(GlobalNode *node) {
+	NodeStore store(node);
 	for (uint i = 0; i < node->names->size(); i++) {
 		registerMethodVar(*(*node->names)[i], kVarGlobal);
 	}
@@ -836,6 +848,7 @@ bool LingoCompiler::visitGlobalNode(GlobalNode *node) {
 /* PropertyNode */
 
 bool LingoCompiler::visitPropertyNode(PropertyNode *node) {
+	NodeStore store(node);
 	for (uint i = 0; i < node->names->size(); i++) {
 		registerMethodVar(*(*node->names)[i], kVarProperty);
 	}
@@ -845,6 +858,7 @@ bool LingoCompiler::visitPropertyNode(PropertyNode *node) {
 /* InstanceNode */
 
 bool LingoCompiler::visitInstanceNode(InstanceNode *node) {
+	NodeStore store(node);
 	for (uint i = 0; i < node->names->size(); i++) {
 		registerMethodVar(*(*node->names)[i], kVarInstance);
 	}
@@ -854,6 +868,7 @@ bool LingoCompiler::visitInstanceNode(InstanceNode *node) {
 /* IfStmtNode */
 
 bool LingoCompiler::visitIfStmtNode(IfStmtNode *node) {
+	NodeStore store(node);
 	COMPILE(node->cond);
 	uint jzPos = _currentAssembly->size();
 	code2(LC::c_jumpifz, nullptr);
@@ -870,6 +885,7 @@ bool LingoCompiler::visitIfStmtNode(IfStmtNode *node) {
 /* IfElseStmtNode */
 
 bool LingoCompiler::visitIfElseStmtNode(IfElseStmtNode *node) {
+	NodeStore store(node);
 	COMPILE(node->cond);
 	uint jzPos = _currentAssembly->size();
 	code2(LC::c_jumpifz, nullptr);
@@ -895,6 +911,7 @@ bool LingoCompiler::visitIfElseStmtNode(IfElseStmtNode *node) {
 /* RepeatWhileNode */
 
 bool LingoCompiler::visitRepeatWhileNode(RepeatWhileNode *node) {
+	NodeStore store(node);
 	LoopNode *prevLoop = _currentLoop;
 	_currentLoop = node;
 
@@ -924,6 +941,7 @@ bool LingoCompiler::visitRepeatWhileNode(RepeatWhileNode *node) {
 /* RepeatWithToNode */
 
 bool LingoCompiler::visitRepeatWithToNode(RepeatWithToNode *node) {
+	NodeStore store(node);
 	LoopNode *prevLoop = _currentLoop;
 	_currentLoop = node;
 
@@ -975,6 +993,7 @@ bool LingoCompiler::visitRepeatWithToNode(RepeatWithToNode *node) {
 /* RepeatWithInNode */
 
 bool LingoCompiler::visitRepeatWithInNode(RepeatWithInNode *node) {
+	NodeStore store(node);
 	LoopNode *prevLoop = _currentLoop;
 	_currentLoop = node;
 
@@ -1030,6 +1049,7 @@ bool LingoCompiler::visitRepeatWithInNode(RepeatWithInNode *node) {
 /* NextRepeatNode */
 
 bool LingoCompiler::visitNextRepeatNode(NextRepeatNode *node) {
+	NodeStore store(node);
 	if (!_currentLoop) {
 		warning("BUILDBOT: LingoCompiler::visitNextRepeatNode: next repeat not inside repeat loop");
 		return false;
@@ -1042,6 +1062,7 @@ bool LingoCompiler::visitNextRepeatNode(NextRepeatNode *node) {
 /* ExitRepeatNode */
 
 bool LingoCompiler::visitExitRepeatNode(ExitRepeatNode *node) {
+	NodeStore store(node);
 	if (!_currentLoop) {
 		warning("BUILDBOT: LingoCompiler::visitExitRepeatLoop: exit repeat not inside repeat loop");
 		return false;
@@ -1054,6 +1075,7 @@ bool LingoCompiler::visitExitRepeatNode(ExitRepeatNode *node) {
 /* ExitNode */
 
 bool LingoCompiler::visitExitNode(ExitNode *node) {
+	NodeStore store(node);
 	code1(LC::c_procret);
 	return true;
 }
@@ -1061,6 +1083,7 @@ bool LingoCompiler::visitExitNode(ExitNode *node) {
 /* ReturnNode */
 
 bool LingoCompiler::visitReturnNode(ReturnNode *node) {
+	NodeStore store(node);
 	if (node->expr) {
 		COMPILE_REF(node->expr);
 		codeCmd("return", 1);
@@ -1074,6 +1097,7 @@ bool LingoCompiler::visitReturnNode(ReturnNode *node) {
 /* TellNode */
 
 bool LingoCompiler::visitTellNode(TellNode *node) {
+	NodeStore store(node);
 	COMPILE(node->target);
 	code1(LC::c_tell);
 	COMPILE_LIST(node->stmts);
@@ -1084,6 +1108,7 @@ bool LingoCompiler::visitTellNode(TellNode *node) {
 /* WhenNode */
 
 bool LingoCompiler::visitWhenNode(WhenNode *node) {
+	NodeStore store(node);
 	code1(LC::c_stringpush);
 	codeString(node->code->c_str());
 	code1(LC::c_whencode);
@@ -1094,6 +1119,7 @@ bool LingoCompiler::visitWhenNode(WhenNode *node) {
 /* DeleteNode */
 
 bool LingoCompiler::visitDeleteNode(DeleteNode *node) {
+	NodeStore store(node);
 	COMPILE_REF(node->chunk);
 	code1(LC::c_delete);
 	return true;
@@ -1102,6 +1128,7 @@ bool LingoCompiler::visitDeleteNode(DeleteNode *node) {
 /* HiliteNode */
 
 bool LingoCompiler::visitHiliteNode(HiliteNode *node) {
+	NodeStore store(node);
 	COMPILE_REF(node->chunk);
 	code1(LC::c_hilite);
 	return true;
@@ -1110,6 +1137,7 @@ bool LingoCompiler::visitHiliteNode(HiliteNode *node) {
 /* AssertErrorNode */
 
 bool LingoCompiler::visitAssertErrorNode(AssertErrorNode *node) {
+	NodeStore store(node);
 	code1(LC::c_asserterror);
 	COMPILE(node->stmt);
 	code1(LC::c_asserterrordone);
@@ -1119,6 +1147,7 @@ bool LingoCompiler::visitAssertErrorNode(AssertErrorNode *node) {
 /* IntNode */
 
 bool LingoCompiler::visitIntNode(IntNode *node) {
+	NodeStore store(node);
 	code1(LC::c_intpush);
 	codeInt(node->val);
 	return true;
@@ -1127,6 +1156,7 @@ bool LingoCompiler::visitIntNode(IntNode *node) {
 /* FloatNode */
 
 bool LingoCompiler::visitFloatNode(FloatNode *node) {
+	NodeStore store(node);
 	code1(LC::c_floatpush);
 	codeFloat(node->val);
 	return true;
@@ -1135,6 +1165,7 @@ bool LingoCompiler::visitFloatNode(FloatNode *node) {
 /* SymbolNode */
 
 bool LingoCompiler::visitSymbolNode(SymbolNode *node) {
+	NodeStore store(node);
 	code1(LC::c_symbolpush);
 	codeString(node->val->c_str());
 	return true;
@@ -1143,6 +1174,7 @@ bool LingoCompiler::visitSymbolNode(SymbolNode *node) {
 /* StringNode */
 
 bool LingoCompiler::visitStringNode(StringNode *node) {
+	NodeStore store(node);
 	code1(LC::c_stringpush);
 	codeString(node->val->c_str());
 	return true;
@@ -1151,6 +1183,7 @@ bool LingoCompiler::visitStringNode(StringNode *node) {
 /* ListNode */
 
 bool LingoCompiler::visitListNode(ListNode *node) {
+	NodeStore store(node);
 	COMPILE_LIST(node->items);
 	code1(LC::c_arraypush);
 	codeInt(node->items->size());
@@ -1160,6 +1193,7 @@ bool LingoCompiler::visitListNode(ListNode *node) {
 /* PropListNode */
 
 bool LingoCompiler::visitPropListNode(PropListNode *node) {
+	NodeStore store(node);
 	bool refModeStore = _refMode;
 	_refMode = false;
 	bool success = true;
@@ -1187,6 +1221,7 @@ bool LingoCompiler::visitPropListNode(PropListNode *node) {
 /* PropPairNode */
 
 bool LingoCompiler::visitPropPairNode(PropPairNode *node) {
+	NodeStore store(node);
 	COMPILE(node->key);
 	COMPILE(node->val);
 	return true;
@@ -1195,6 +1230,7 @@ bool LingoCompiler::visitPropPairNode(PropPairNode *node) {
 /* FuncNode */
 
 bool LingoCompiler::visitFuncNode(FuncNode *node) {
+	NodeStore store(node);
 	if (node->name->equalsIgnoreCase("field") && node->args->size() >= 1) {
 		COMPILE((*node->args)[0]);
 		if (_refMode) {
@@ -1221,6 +1257,7 @@ bool LingoCompiler::visitFuncNode(FuncNode *node) {
 /* VarNode */
 
 bool LingoCompiler::visitVarNode(VarNode *node) {
+	NodeStore store(node);
 	if (g_director->getVersion() < 400 || (g_director->getCurrentMovie() && g_director->getCurrentMovie()->_allowOutdatedLingo)) {
 		int val = castNumToNum(node->name->c_str());
 		if (val != -1) {
@@ -1245,6 +1282,7 @@ bool LingoCompiler::visitVarNode(VarNode *node) {
 /* ParensNode */
 
 bool LingoCompiler::visitParensNode(ParensNode *node) {
+	NodeStore store(node);
 	COMPILE(node->expr);
 	return true;
 }
@@ -1252,6 +1290,7 @@ bool LingoCompiler::visitParensNode(ParensNode *node) {
 /* UnaryOpNode */
 
 bool LingoCompiler::visitUnaryOpNode(UnaryOpNode *node) {
+	NodeStore store(node);
 	COMPILE(node->arg);
 	code1(node->op);
 	return true;
@@ -1260,6 +1299,7 @@ bool LingoCompiler::visitUnaryOpNode(UnaryOpNode *node) {
 /* BinaryOpNode */
 
 bool LingoCompiler::visitBinaryOpNode(BinaryOpNode *node) {
+	NodeStore store(node);
 	COMPILE(node->a);
 	COMPILE(node->b);
 	code1(node->op);
@@ -1269,6 +1309,7 @@ bool LingoCompiler::visitBinaryOpNode(BinaryOpNode *node) {
 /* FrameNode */
 
 bool LingoCompiler::visitFrameNode(FrameNode *node) {
+	NodeStore store(node);
 	COMPILE(node->arg);
 	return true;
 }
@@ -1276,6 +1317,7 @@ bool LingoCompiler::visitFrameNode(FrameNode *node) {
 /* MovieNode */
 
 bool LingoCompiler::visitMovieNode(MovieNode *node) {
+	NodeStore store(node);
 	COMPILE(node->arg);
 	return true;
 }
@@ -1283,6 +1325,7 @@ bool LingoCompiler::visitMovieNode(MovieNode *node) {
 /* IntersectsNode */
 
 bool LingoCompiler::visitIntersectsNode(IntersectsNode *node) {
+	NodeStore store(node);
 	COMPILE(node->sprite1);
 	COMPILE(node->sprite2);
 	code1(LC::c_intersects);
@@ -1292,6 +1335,7 @@ bool LingoCompiler::visitIntersectsNode(IntersectsNode *node) {
 /* WithinNode */
 
 bool LingoCompiler::visitWithinNode(WithinNode *node) {
+	NodeStore store(node);
 	COMPILE(node->sprite1);
 	COMPILE(node->sprite2);
 	code1(LC::c_within);
@@ -1301,6 +1345,7 @@ bool LingoCompiler::visitWithinNode(WithinNode *node) {
 /* TheNode */
 
 bool LingoCompiler::visitTheNode(TheNode *node) {
+	NodeStore store(node);
 	if (g_lingo->_theEntities.contains(*node->prop) && !g_lingo->_theEntities[*node->prop]->hasId) {
 		code1(LC::c_intpush);
 		codeInt(0); // Put dummy id
@@ -1317,6 +1362,7 @@ bool LingoCompiler::visitTheNode(TheNode *node) {
 /* TheOfNode */
 
 bool LingoCompiler::visitTheOfNode(TheOfNode *node) {
+	NodeStore store(node);
 	switch (node->obj->type) {
 	case kChunkExprNode:
 		{
@@ -1451,6 +1497,7 @@ bool LingoCompiler::visitTheOfNode(TheOfNode *node) {
 /* TheNumberOfNode */
 
 bool LingoCompiler::visitTheNumberOfNode(TheNumberOfNode *node) {
+	NodeStore store(node);
 	switch (node->type) {
 	case kNumberOfCastlibs:
 		codeInt(0); // Put dummy id
@@ -1506,6 +1553,7 @@ bool LingoCompiler::visitTheNumberOfNode(TheNumberOfNode *node) {
 /* TheLastNode */
 
 bool LingoCompiler::visitTheLastNode(TheLastNode *node) {
+	NodeStore store(node);
 	code1(LC::c_intpush);
 	codeInt(-30000);
 	code1(LC::c_intpush);
@@ -1551,6 +1599,7 @@ bool LingoCompiler::visitTheLastNode(TheLastNode *node) {
 /* TheDateTimeNode */
 
 bool LingoCompiler::visitTheDateTimeNode(TheDateTimeNode *node) {
+	NodeStore store(node);
 	code1(LC::c_intpush);
 	codeInt(0); // Put dummy id
 	code1(LC::c_theentitypush);
@@ -1562,30 +1611,35 @@ bool LingoCompiler::visitTheDateTimeNode(TheDateTimeNode *node) {
 /* MenuNode */
 
 bool LingoCompiler::visitMenuNode(MenuNode *node) {
+	NodeStore store(node);
 	return true;
 }
 
 /* MenuItemNode */
 
 bool LingoCompiler::visitMenuItemNode(MenuItemNode *node) {
+	NodeStore store(node);
 	return true;
 }
 
 /* SoundItem */
 
 bool LingoCompiler::visitSoundNode(SoundNode *node) {
+	NodeStore store(node);
 	return true;
 }
 
 /* SpriteNode */
 
 bool LingoCompiler::visitSpriteNode(SpriteNode *node) {
+	NodeStore store(node);
 	return true;
 }
 
 /* ChunkExprNode */
 
 bool LingoCompiler::visitChunkExprNode(ChunkExprNode *node) {
+	NodeStore store(node);
 	COMPILE(node->start);
 	if (node->end) {
 		COMPILE(node->end);
diff --git a/engines/director/lingo/lingo-codegen.h b/engines/director/lingo/lingo-codegen.h
index 1a71035c8e7..88bd91ba042 100644
--- a/engines/director/lingo/lingo-codegen.h
+++ b/engines/director/lingo/lingo-codegen.h
@@ -55,7 +55,7 @@ public:
 
 	LingoArchive *_assemblyArchive;
 	ScriptContext *_assemblyContext;
-	Node *_assemblyAST;
+	Common::SharedPtr<Node> _assemblyAST;
 	int32 _assemblyId;
 	ScriptData *_currentAssembly;
 	bool _indef;
diff --git a/engines/director/lingo/lingo-gr.cpp b/engines/director/lingo/lingo-gr.cpp
index 23b1d72a045..900fa9c2490 100644
--- a/engines/director/lingo/lingo-gr.cpp
+++ b/engines/director/lingo/lingo-gr.cpp
@@ -2865,7 +2865,7 @@ yyreduce:
     {
   case 2: /* script: scriptpartlist  */
 #line 208 "engines/director/lingo/lingo-gr.y"
-                                                        { g_lingo->_compiler->_assemblyAST = new ScriptNode((yyvsp[0].nodelist)); (yyval.node) = nullptr; }
+                                                        { g_lingo->_compiler->_assemblyAST = Common::SharedPtr<Node>(new ScriptNode((yyvsp[0].nodelist))); (yyval.node) = nullptr; }
 #line 2870 "engines/director/lingo/lingo-gr.cpp"
     break;
 
diff --git a/engines/director/lingo/lingo-gr.y b/engines/director/lingo/lingo-gr.y
index c818f154a70..684d3711363 100644
--- a/engines/director/lingo/lingo-gr.y
+++ b/engines/director/lingo/lingo-gr.y
@@ -205,7 +205,7 @@ static void checkEnd(Common::String *token, Common::String *expect, bool require
 
 // TOP-LEVEL STUFF
 
-script: scriptpartlist					{ g_lingo->_compiler->_assemblyAST = new ScriptNode($scriptpartlist); $$ = nullptr; } ;
+script: scriptpartlist					{ g_lingo->_compiler->_assemblyAST = Common::SharedPtr<Node>(new ScriptNode($scriptpartlist)); $$ = nullptr; } ;
 
 scriptpartlist: scriptpart[item]				{
 		NodeList *list = new NodeList;
diff --git a/engines/director/lingo/lingo-object.cpp b/engines/director/lingo/lingo-object.cpp
index 53737a63a4c..03f4b7b5ac1 100644
--- a/engines/director/lingo/lingo-object.cpp
+++ b/engines/director/lingo/lingo-object.cpp
@@ -425,7 +425,6 @@ ScriptContext::ScriptContext(const ScriptContext &sc) : Object<ScriptContext>(sc
 }
 
 ScriptContext::~ScriptContext() {
-	delete _assemblyAST;
 }
 
 Common::String ScriptContext::asString() {
diff --git a/engines/director/lingo/lingo-object.h b/engines/director/lingo/lingo-object.h
index 47bfb69dffa..f61e0a81b1d 100644
--- a/engines/director/lingo/lingo-object.h
+++ b/engines/director/lingo/lingo-object.h
@@ -216,7 +216,7 @@ public:
 	Common::Array<Datum> _constants;
 	Common::HashMap<uint32, Datum> _objArray;
 	MethodHash _methodNames;
-	Node *_assemblyAST = nullptr;	// Optionally contains AST when we compile Lingo
+	Common::SharedPtr<Node> _assemblyAST;	// Optionally contains AST when we compile Lingo
 
 private:
 	DatumHash _properties;


Commit: 23c472d3b815dbc65937ce5f40695a8a4b8c84c8
    https://github.com/scummvm/scummvm/commit/23c472d3b815dbc65937ce5f40695a8a4b8c84c8
Author: scemino (scemino74 at gmail.com)
Date: 2024-06-01T07:51:02+02:00

Commit Message:
DIRECTOR: Add indentation in script for D2 and D3

Changed paths:
    engines/director/debugtools.cpp


diff --git a/engines/director/debugtools.cpp b/engines/director/debugtools.cpp
index a66a9a21606..c7ea39510e9 100644
--- a/engines/director/debugtools.cpp
+++ b/engines/director/debugtools.cpp
@@ -473,7 +473,7 @@ const LingoDec::Handler *getHandler(CastMemberID id, const Common::String &handl
 	return getHandler(movie->getSharedCast(), id, handlerId);
 }
 
-ImGuiScript toImGuiScript(ScriptType scriptType, CastMemberID id, const Common::String &handlerId) {
+static ImGuiScript toImGuiScript(ScriptType scriptType, CastMemberID id, const Common::String &handlerId) {
 	ImGuiScript result;
 	result.id = id;
 	result.handlerId = handlerId;
@@ -522,6 +522,7 @@ static Director::Breakpoint *getBreakpoint(const Common::String &handlerName, ui
 class RenderOldScriptVisitor : public NodeVisitor {
 private:
 	ImGuiScript &_script;
+	int _indent = 0;
 
 public:
 	explicit RenderOldScriptVisitor(ImGuiScript &script) : _script(script) {}
@@ -543,11 +544,15 @@ public:
 			}
 			ImGui::NewLine();
 		}
+		indent();
 		for (uint i = 0; i < node->stmts->size(); i++) {
+			renderIndentation();
 			Node *stmt = (*node->stmts)[i];
 			stmt->accept(this);
 			ImGui::NewLine();
 		}
+		unindent();
+		renderIndentation();
 		ImGui::Text("end");
 		return true;
 	}
@@ -561,16 +566,21 @@ public:
 		ImGui::PopStyleVar();
 		return true;
 	}
+
 	virtual bool visitFactoryNode(FactoryNode *node){
 		ImGui::Text("factory %s", node->name->c_str());
-		ImGui::SameLine();
+		ImGui::NewLine();
+		indent();
 		for (uint i = 0; i < node->methods->size(); i++) {
+			renderIndentation();
 			Node *method = (*node->methods)[i];
 			method->accept(this);
 			ImGui::NewLine();
 		}
+		unindent();
 		return true;
 	}
+
 	virtual bool visitCmdNode(CmdNode *node) {
 		ImGui::Text("%s ", node->name->c_str());
 		ImGui::SameLine();
@@ -591,6 +601,7 @@ public:
 		}
 		return true;
 	}
+
 	virtual bool visitPutIntoNode(PutIntoNode *node){
 		ImGui::Text("put ");
 		ImGui::SameLine();
@@ -600,6 +611,7 @@ public:
 		node->var->accept(this);
 		return true;
 	}
+
 	virtual bool visitPutAfterNode(PutAfterNode *node) {
 		ImGui::Text("put ");
 		ImGui::SameLine();
@@ -609,6 +621,7 @@ public:
 		node->var->accept(this);
 		return true;
 	}
+
 	virtual bool visitPutBeforeNode(PutBeforeNode *node){
 		ImGui::Text("put ");
 		ImGui::SameLine();
@@ -618,6 +631,7 @@ public:
 		node->var->accept(this);
 		return true;
 	}
+
 	virtual bool visitSetNode(SetNode *node){
 		ImGui::Text("set ");
 		ImGui::SameLine();
@@ -643,17 +657,20 @@ public:
 	}
 
 	virtual bool visitGlobalNode(GlobalNode *node){
-		displayDefineVar("global ", node->names);
+		displayDefineVar("global", node->names);
 		return true;
 	}
+
 	virtual bool visitPropertyNode(PropertyNode *node){
-		displayDefineVar("property ", node->names);
+		displayDefineVar("property", node->names);
 		return true;
 	}
+
 	virtual bool visitInstanceNode(InstanceNode *node){
-		displayDefineVar("instance ", node->names);
+		displayDefineVar("instance", node->names);
 		return true;
 	}
+
 	virtual bool visitIfStmtNode(IfStmtNode *node){
 		ImGui::Text("if ");
 		ImGui::SameLine();
@@ -663,16 +680,21 @@ public:
 			ImGui::SameLine();
 			(*node->stmts)[0]->accept(this);
 		} else {
+			indent();
 			for (uint i = 0; i < node->stmts->size(); i++) {
+				renderIndentation();
 				Node *stmt = (*node->stmts)[i];
 				stmt->accept(this);
 				ImGui::NewLine();
 			}
+			unindent();
+			renderIndentation();
 			ImGui::Text("endif");
 			ImGui::SameLine();
 		}
 		return true;
 	}
+
 	virtual bool visitIfElseStmtNode(IfElseStmtNode *node){
 		ImGui::Text("if ");
 		ImGui::SameLine();
@@ -684,40 +706,54 @@ public:
 			ImGui::Text(" ");
 			ImGui::SameLine();
 		} else {
+			indent();
 			for (uint i = 0; i < node->stmts1->size(); i++) {
+				renderIndentation();
 				Node *stmt = (*node->stmts1)[i];
 				stmt->accept(this);
 				ImGui::NewLine();
 			}
+			unindent();
+			renderIndentation();
 		}
 		ImGui::Text("else ");
 		if(node->stmts2->size() == 1) {
 			ImGui::SameLine();
 			(*node->stmts2)[0]->accept(this);
 		} else {
+			indent();
 			for (uint i = 0; i < node->stmts2->size(); i++) {
+				renderIndentation();
 				Node *stmt = (*node->stmts2)[i];
 				stmt->accept(this);
 				ImGui::NewLine();
 			}
+			unindent();
+			renderIndentation();
 			ImGui::Text("endif");
 			ImGui::SameLine();
 		}
 		return true;
 	}
+
 	virtual bool visitRepeatWhileNode(RepeatWhileNode *node){
 		ImGui::Text("repeat while ");
 		ImGui::SameLine();
 		node->cond->accept(this);
 		ImGui::NewLine();
+		indent();
 		for (uint i = 0; i < node->stmts->size(); i++) {
+			renderIndentation();
 			Node *stmt = (*node->stmts)[i];
 			stmt->accept(this);
 			ImGui::NewLine();
 		}
+		unindent();
+		renderIndentation();
 		ImGui::Text("endrepeat");
 		return true;
 	}
+
 	virtual bool visitRepeatWithToNode(RepeatWithToNode *node){
 		ImGui::Text("repeat with ");
 		ImGui::SameLine();
@@ -727,14 +763,19 @@ public:
 		ImGui::Text(" %s ", node->down ? "down to" : "to");
 		node->end->accept(this);
 		ImGui::NewLine();
+		indent();
 		for (uint i = 0; i < node->stmts->size(); i++) {
+			renderIndentation();
 			Node *stmt = (*node->stmts)[i];
 			stmt->accept(this);
 			ImGui::NewLine();
 		}
+		unindent();
+		renderIndentation();
 		ImGui::Text("endrepeat");
 		return true;
 	}
+
 	virtual bool visitRepeatWithInNode(RepeatWithInNode *node){
 		ImGui::Text("repeat with ");
 		ImGui::SameLine();
@@ -742,26 +783,34 @@ public:
 		ImGui::SameLine();
 		node->list->accept(this);
 		ImGui::NewLine();
+		indent();
 		for (uint i = 0; i < node->stmts->size(); i++) {
+			renderIndentation();
 			Node *stmt = (*node->stmts)[i];
 			stmt->accept(this);
 			ImGui::NewLine();
 		}
+		unindent();
+		renderIndentation();
 		ImGui::Text("endrepeat");
 		return true;
 	}
+
 	virtual bool visitNextRepeatNode(NextRepeatNode *node){
 		ImGui::Text("next repeat");
 		return true;
 	}
+
 	virtual bool visitExitRepeatNode(ExitRepeatNode *node){
 		ImGui::Text("exit repeat");
 		return true;
 	}
+
 	virtual bool visitExitNode(ExitNode *node){
 		ImGui::Text("exit");
 		return true;
 	}
+
 	virtual bool visitReturnNode(ReturnNode *node){
 		ImGui::Text("return");
 		if(node->expr) {
@@ -772,6 +821,7 @@ public:
 		}
 		return true;
 	}
+
 	virtual bool visitTellNode(TellNode *node){
 		ImGui::Text("tell ");
 		node->target->accept(this);
@@ -781,58 +831,71 @@ public:
 			ImGui::SameLine();
 			(*node->stmts)[0]->accept(this);
 		} else {
+			indent();
 			for (uint i = 0; i < node->stmts->size(); i++) {
+				renderIndentation();
 				Node *stmt = (*node->stmts)[i];
 				stmt->accept(this);
 				ImGui::NewLine();
 			}
+			unindent();
+			renderIndentation();
 			ImGui::Text("endtell");
 		}
 		return true;
 	}
+
 	virtual bool visitWhenNode(WhenNode *node){
 		ImGui::Text("when %s then %s", node->event->c_str(), node->code->c_str());
 		ImGui::SameLine();
 		return true;
 	}
+
 	virtual bool visitDeleteNode(DeleteNode *node){
 		ImGui::Text("delete ");
 		ImGui::SameLine();
 		node->chunk->accept(this);
 		return true;
 	}
+
 	virtual bool visitHiliteNode(HiliteNode *node){
 		ImGui::Text("hilite ");
 		ImGui::SameLine();
 		node->chunk->accept(this);
 		return true;
 	}
+
 	virtual bool visitAssertErrorNode(AssertErrorNode *node){
 		ImGui::Text("scummvmAssertError ");
 		ImGui::SameLine();
 		node->stmt->accept(this);
 		return true;
 	}
+
 	virtual bool visitIntNode(IntNode *node){
 		ImGui::Text("%d", node->val);
 		ImGui::SameLine();
 		return true;
 	}
+
 	virtual bool visitFloatNode(FloatNode *node){
 		ImGui::Text("%g", node->val);
 		ImGui::SameLine();
 		return true;
 	}
+
 	virtual bool visitSymbolNode(SymbolNode *node){
 		ImGui::Text("%s", node->val->c_str());
 		ImGui::SameLine();
 		return true;
 	}
+
 	virtual bool visitStringNode(StringNode *node){
 		ImGui::Text("\"%s\"", node->val->c_str());
 		ImGui::SameLine();
 		return true;
 	}
+
 	virtual bool visitListNode(ListNode *node){
 		ImGui::Text("[");
 		ImGui::SameLine();
@@ -848,6 +911,7 @@ public:
 		ImGui::SameLine();
 		return true;
 	}
+
 	virtual bool visitPropListNode(PropListNode *node){
 		ImGui::Text("[");
 		ImGui::SameLine();
@@ -868,6 +932,7 @@ public:
 		ImGui::SameLine();
 		return true;
 	}
+
 	virtual bool visitPropPairNode(PropPairNode *node){
 		node->key->accept(this);
 		ImGui::Text(":");
@@ -875,6 +940,7 @@ public:
 		node->val->accept(this);
 		return true;
 	}
+
 	virtual bool visitFuncNode(FuncNode *node){
 		ImGui::Text("%s(", node->name->c_str());
 		ImGui::SameLine();
@@ -890,11 +956,13 @@ public:
 		ImGui::SameLine();
 		return true;
 	}
+
 	virtual bool visitVarNode(VarNode *node){
 		ImGui::Text("%s", node->name->c_str());
 		ImGui::SameLine();
 		return true;
 	}
+
 	virtual bool visitParensNode(ParensNode *node){
 		ImGui::Text("(");
 		ImGui::SameLine();
@@ -903,6 +971,7 @@ public:
 		ImGui::SameLine();
 		return true;
 	}
+
 	virtual bool visitUnaryOpNode(UnaryOpNode *node){
 		char op = '?';
 		if(node->op == LC::c_negate) {
@@ -915,6 +984,7 @@ public:
 		node->arg->accept(this);
 		return true;
 	}
+
 	virtual bool visitBinaryOpNode(BinaryOpNode *node){
 		node->a->accept(this);
 		static struct {
@@ -949,18 +1019,21 @@ public:
 		node->b->accept(this);
 		return true;
 	 }
+
 	virtual bool visitFrameNode(FrameNode *node){
 		ImGui::Text("frame ");
 		ImGui::SameLine();
 		node->arg->accept(this);
 		return true;
 	}
+
 	virtual bool visitMovieNode(MovieNode *node){
 		ImGui::Text("movie ");
 		ImGui::SameLine();
 		node->arg->accept(this);
 		return true;
 	}
+
 	virtual bool visitIntersectsNode(IntersectsNode *node){
 		ImGui::Text("sprite ");
 		ImGui::SameLine();
@@ -969,6 +1042,7 @@ public:
 		node->sprite2->accept(this);
 		return true;
 	}
+
 	virtual bool visitWithinNode(WithinNode *node){
 		ImGui::Text("sprite ");
 		ImGui::SameLine();
@@ -977,23 +1051,27 @@ public:
 		node->sprite2->accept(this);
 		return true;
 	}
+
 	virtual bool visitTheNode(TheNode *node){
 		ImGui::Text("the %s", node->prop->c_str());
 		ImGui::SameLine();
 		return true;
 	}
+
 	virtual bool visitTheOfNode(TheOfNode *node){
 		ImGui::Text("the %s of ", node->prop->c_str());
 		ImGui::SameLine();
 		node->obj->accept(this);
 		return true;
 	}
+
 	virtual bool visitTheNumberOfNode(TheNumberOfNode *node){
 		ImGui::Text("the number of ");
 		ImGui::SameLine();
 		node->arg->accept(this);
 		return true;
 	}
+
 	virtual bool visitTheLastNode(TheLastNode *node){
 		// TODO: change the node to know if it's 'in' or 'of'
 		ImGui::Text("the last %s in/of ", toString(node->type).c_str());
@@ -1001,6 +1079,7 @@ public:
 		node->arg->accept(this);
 		return true;
 	}
+
 	virtual bool visitTheDateTimeNode(TheDateTimeNode *node){
 		const char* key1 = "";
 		switch(node->field) {
@@ -1019,12 +1098,14 @@ public:
 		ImGui::SameLine();
 		return true;
 	}
+
 	virtual bool visitMenuNode(MenuNode *node){
 		ImGui::Text("menu ");
 		ImGui::SameLine();
 		node->arg->accept(this);
 		return true;
 	}
+
 	virtual bool visitMenuItemNode(MenuItemNode *node){
 		ImGui::Text("menuitem ");
 		ImGui::SameLine();
@@ -1034,18 +1115,21 @@ public:
 		node->arg2->accept(this);
 		return true;
 	}
+
 	virtual bool visitSoundNode(SoundNode *node){
 		ImGui::Text("sound ");
 		ImGui::SameLine();
 		node->arg->accept(this);
 		return true;
 	}
+
 	virtual bool visitSpriteNode(SpriteNode *node){
 		ImGui::Text("sprite ");
 		ImGui::SameLine();
 		node->arg->accept(this);
 		return true;
 	}
+
 	virtual bool visitChunkExprNode(ChunkExprNode *node){
 		const char* key1 = "";
 		switch(node->type) {
@@ -1091,8 +1175,23 @@ private:
 		}
 		return "<unknown>";
 	}
-};
 
+	void indent() {
+		_indent++;
+	}
+
+	void unindent() {
+		if (_indent > 0)
+			_indent--;
+	}
+
+	void renderIndentation() const {
+		for (int i = 0; i < _indent; i++) {
+			ImGui::Text("  ");
+			ImGui::SameLine();
+		}
+	}
+};
 class RenderScriptVisitor : public LingoDec::NodeVisitor {
 public:
 	RenderScriptVisitor(ImGuiScript &script, bool showByteCode) : _script(script), _showByteCode(showByteCode) {


Commit: f596c31c95abfbe0c6e4dd5aebd27910adb6cc31
    https://github.com/scummvm/scummvm/commit/f596c31c95abfbe0c6e4dd5aebd27910adb6cc31
Author: scemino (scemino74 at gmail.com)
Date: 2024-06-01T07:51:02+02:00

Commit Message:
DIRECTOR: Add syntax highlighting in script for D2 and D3

Changed paths:
    engines/director/debugtools.cpp


diff --git a/engines/director/debugtools.cpp b/engines/director/debugtools.cpp
index c7ea39510e9..2fbabedfd2c 100644
--- a/engines/director/debugtools.cpp
+++ b/engines/director/debugtools.cpp
@@ -523,13 +523,24 @@ class RenderOldScriptVisitor : public NodeVisitor {
 private:
 	ImGuiScript &_script;
 	int _indent = 0;
+	bool _isScriptInDebug = false;
+	bool _currentStatementDisplayed = false;
 
 public:
-	explicit RenderOldScriptVisitor(ImGuiScript &script) : _script(script) {}
+	explicit RenderOldScriptVisitor(ImGuiScript &script) : _script(script) {
+		Common::Array<CFrame *> &callstack = g_lingo->_state->callstack;
+		if (!callstack.empty()) {
+			CFrame *head = callstack[callstack.size() - 1];
+			_isScriptInDebug = (head->sp.ctx->_id == script.id.member) && (*head->sp.name == script.handlerId);
+		}
+		_script.startOffsets.clear();
+	}
 
 	virtual bool visitHandlerNode(HandlerNode *node) {
-		ImGui::Text("on %s", node->name->c_str());
-		if(!node->args->empty()) {
+		ImGui::Text("on ");
+		ImGui::SameLine();
+		ImGui::TextColored(_state->_colors._call_color, "%s", node->name->c_str());
+		if (!node->args->empty()) {
 			ImGui::SameLine();
 			ImGui::Text(" ");
 			ImGui::SameLine();
@@ -546,34 +557,36 @@ public:
 		}
 		indent();
 		for (uint i = 0; i < node->stmts->size(); i++) {
-			renderIndentation();
 			Node *stmt = (*node->stmts)[i];
+			renderLine(stmt->startOffset);
 			stmt->accept(this);
 			ImGui::NewLine();
 		}
 		unindent();
-		renderIndentation();
-		ImGui::Text("end");
+		renderLine(node->endOffset);
+		ImGui::TextColored(_state->_colors._keyword_color, "end");
 		return true;
 	}
 
-	virtual bool visitScriptNode(ScriptNode *node){
+	virtual bool visitScriptNode(ScriptNode *node) {
 		ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2());
-		for(Node* child : *node->children) {
-			if(child->type == kHandlerNode && *((HandlerNode*)child)->name != _script.handlerId) continue;
+		for (Node *child : *node->children) {
+			if (child->type == kHandlerNode && *((HandlerNode *)child)->name != _script.handlerId)
+				continue;
+			renderLine(child->startOffset);
 			child->accept(this);
 		}
 		ImGui::PopStyleVar();
 		return true;
 	}
 
-	virtual bool visitFactoryNode(FactoryNode *node){
+	virtual bool visitFactoryNode(FactoryNode *node) {
 		ImGui::Text("factory %s", node->name->c_str());
 		ImGui::NewLine();
 		indent();
 		for (uint i = 0; i < node->methods->size(); i++) {
-			renderIndentation();
 			Node *method = (*node->methods)[i];
+			renderLine(method->startOffset);
 			method->accept(this);
 			ImGui::NewLine();
 		}
@@ -584,8 +597,8 @@ public:
 	virtual bool visitCmdNode(CmdNode *node) {
 		ImGui::Text("%s ", node->name->c_str());
 		ImGui::SameLine();
-		if(*node->name == "go") {
-			ImGui::Text("to ");
+		if (*node->name == "go") {
+			ImGui::TextColored(_state->_colors._keyword_color, "to ");
 			ImGui::SameLine();
 		}
 		for (uint i = 0; i < node->args->size(); i++) {
@@ -602,41 +615,41 @@ public:
 		return true;
 	}
 
-	virtual bool visitPutIntoNode(PutIntoNode *node){
-		ImGui::Text("put ");
+	virtual bool visitPutIntoNode(PutIntoNode *node) {
+		ImGui::TextColored(_state->_colors._keyword_color, "put ");
 		ImGui::SameLine();
 		node->val->accept(this);
-		ImGui::Text(" into ");
+		ImGui::TextColored(_state->_colors._keyword_color, " into ");
 		ImGui::SameLine();
 		node->var->accept(this);
 		return true;
 	}
 
 	virtual bool visitPutAfterNode(PutAfterNode *node) {
-		ImGui::Text("put ");
+		ImGui::TextColored(_state->_colors._keyword_color, "put ");
 		ImGui::SameLine();
 		node->val->accept(this);
-		ImGui::Text(" after ");
+		ImGui::TextColored(_state->_colors._keyword_color, " after ");
 		ImGui::SameLine();
 		node->var->accept(this);
 		return true;
 	}
 
-	virtual bool visitPutBeforeNode(PutBeforeNode *node){
-		ImGui::Text("put ");
+	virtual bool visitPutBeforeNode(PutBeforeNode *node) {
+		ImGui::TextColored(_state->_colors._keyword_color, "put ");
 		ImGui::SameLine();
 		node->val->accept(this);
-		ImGui::Text(" before ");
+		ImGui::TextColored(_state->_colors._keyword_color, " before ");
 		ImGui::SameLine();
 		node->var->accept(this);
 		return true;
 	}
 
-	virtual bool visitSetNode(SetNode *node){
-		ImGui::Text("set ");
+	virtual bool visitSetNode(SetNode *node) {
+		ImGui::TextColored(_state->_colors._keyword_color, "set ");
 		ImGui::SameLine();
 		node->val->accept(this);
-		ImGui::Text(" to ");
+		ImGui::TextColored(_state->_colors._keyword_color, " to ");
 		ImGui::SameLine();
 		node->var->accept(this);
 		return true;
@@ -656,128 +669,134 @@ public:
 		}
 	}
 
-	virtual bool visitGlobalNode(GlobalNode *node){
+	virtual bool visitGlobalNode(GlobalNode *node) {
 		displayDefineVar("global", node->names);
 		return true;
 	}
 
-	virtual bool visitPropertyNode(PropertyNode *node){
+	virtual bool visitPropertyNode(PropertyNode *node) {
 		displayDefineVar("property", node->names);
 		return true;
 	}
 
-	virtual bool visitInstanceNode(InstanceNode *node){
+	virtual bool visitInstanceNode(InstanceNode *node) {
 		displayDefineVar("instance", node->names);
 		return true;
 	}
 
-	virtual bool visitIfStmtNode(IfStmtNode *node){
-		ImGui::Text("if ");
+	virtual bool visitIfStmtNode(IfStmtNode *node) {
+		ImGui::TextColored(_state->_colors._keyword_color, "if ");
 		ImGui::SameLine();
 		node->cond->accept(this);
-		ImGui::Text(" then ");
-		if(node->stmts->size() == 1) {
+		ImGui::TextColored(_state->_colors._keyword_color, " then ");
+		if (node->stmts->size() == 1) {
 			ImGui::SameLine();
 			(*node->stmts)[0]->accept(this);
 		} else {
 			indent();
 			for (uint i = 0; i < node->stmts->size(); i++) {
-				renderIndentation();
 				Node *stmt = (*node->stmts)[i];
+				renderLine(stmt->startOffset);
 				stmt->accept(this);
 				ImGui::NewLine();
 			}
 			unindent();
-			renderIndentation();
-			ImGui::Text("endif");
+			renderLine(node->endOffset);
+			ImGui::TextColored(_state->_colors._keyword_color, "endif");
 			ImGui::SameLine();
 		}
 		return true;
 	}
 
-	virtual bool visitIfElseStmtNode(IfElseStmtNode *node){
-		ImGui::Text("if ");
+	virtual bool visitIfElseStmtNode(IfElseStmtNode *node) {
+		ImGui::TextColored(_state->_colors._keyword_color, "if ");
 		ImGui::SameLine();
 		node->cond->accept(this);
-		ImGui::Text(" then ");
-		if(node->stmts1->size() == 1) {
+		ImGui::TextColored(_state->_colors._keyword_color, " then ");
+		if (node->stmts1->size() == 1) {
 			ImGui::SameLine();
 			(*node->stmts1)[0]->accept(this);
 			ImGui::Text(" ");
 			ImGui::SameLine();
 		} else {
+			uint offset = node->cond->endOffset;
 			indent();
 			for (uint i = 0; i < node->stmts1->size(); i++) {
-				renderIndentation();
 				Node *stmt = (*node->stmts1)[i];
+				renderLine(stmt->startOffset);
 				stmt->accept(this);
 				ImGui::NewLine();
+				offset = stmt->endOffset;
 			}
 			unindent();
-			renderIndentation();
+			renderLine(offset);
 		}
-		ImGui::Text("else ");
-		if(node->stmts2->size() == 1) {
+		ImGui::TextColored(_state->_colors._keyword_color, "else ");
+		if (node->stmts2->size() == 1) {
 			ImGui::SameLine();
 			(*node->stmts2)[0]->accept(this);
 		} else {
+			uint offset = node->cond->endOffset;
 			indent();
 			for (uint i = 0; i < node->stmts2->size(); i++) {
-				renderIndentation();
 				Node *stmt = (*node->stmts2)[i];
+				renderLine(stmt->startOffset);
 				stmt->accept(this);
 				ImGui::NewLine();
+				offset = stmt->endOffset;
 			}
 			unindent();
-			renderIndentation();
-			ImGui::Text("endif");
+			renderLine(offset);
+			ImGui::TextColored(_state->_colors._keyword_color, "endif");
 			ImGui::SameLine();
 		}
 		return true;
 	}
 
-	virtual bool visitRepeatWhileNode(RepeatWhileNode *node){
-		ImGui::Text("repeat while ");
+	virtual bool visitRepeatWhileNode(RepeatWhileNode *node) {
+		ImGui::TextColored(_state->_colors._keyword_color, "repeat while ");
 		ImGui::SameLine();
 		node->cond->accept(this);
 		ImGui::NewLine();
 		indent();
+		uint offset = node->cond->endOffset;
 		for (uint i = 0; i < node->stmts->size(); i++) {
-			renderIndentation();
 			Node *stmt = (*node->stmts)[i];
+			renderLine(stmt->startOffset);
 			stmt->accept(this);
 			ImGui::NewLine();
+			offset = stmt->endOffset;
 		}
 		unindent();
-		renderIndentation();
-		ImGui::Text("endrepeat");
+		renderLine(offset);
+		ImGui::TextColored(_state->_colors._keyword_color, "endrepeat");
 		return true;
 	}
 
-	virtual bool visitRepeatWithToNode(RepeatWithToNode *node){
-		ImGui::Text("repeat with ");
+	virtual bool visitRepeatWithToNode(RepeatWithToNode *node) {
+		ImGui::TextColored(_state->_colors._keyword_color, "repeat with ");
 		ImGui::SameLine();
 		ImGui::Text("%s = ", node->var->c_str());
 		ImGui::SameLine();
 		node->start->accept(this);
-		ImGui::Text(" %s ", node->down ? "down to" : "to");
+		ImGui::TextColored(_state->_colors._keyword_color, " %s ", node->down ? "down to" : "to");
 		node->end->accept(this);
 		ImGui::NewLine();
 		indent();
 		for (uint i = 0; i < node->stmts->size(); i++) {
-			renderIndentation();
 			Node *stmt = (*node->stmts)[i];
+			renderLine(stmt->startOffset);
 			stmt->accept(this);
 			ImGui::NewLine();
 		}
 		unindent();
-		renderIndentation();
-		ImGui::Text("endrepeat");
+		renderLine(node->endOffset);
+		ImGui::TextColored(_state->_colors._keyword_color, "endrepeat");
 		return true;
 	}
 
-	virtual bool visitRepeatWithInNode(RepeatWithInNode *node){
-		ImGui::Text("repeat with ");
+	virtual bool visitRepeatWithInNode(RepeatWithInNode *node) {
+		ImGui::TextColored(_state->_colors._keyword_color, "repeat with ");
 		ImGui::SameLine();
 		ImGui::Text("%s in ", node->var->c_str());
 		ImGui::SameLine();
@@ -785,35 +804,35 @@ public:
 		ImGui::NewLine();
 		indent();
 		for (uint i = 0; i < node->stmts->size(); i++) {
-			renderIndentation();
 			Node *stmt = (*node->stmts)[i];
+			renderLine(stmt->startOffset);
 			stmt->accept(this);
 			ImGui::NewLine();
 		}
 		unindent();
-		renderIndentation();
-		ImGui::Text("endrepeat");
+		renderLine(node->endOffset);
+		ImGui::TextColored(_state->_colors._keyword_color, "endrepeat");
 		return true;
 	}
 
-	virtual bool visitNextRepeatNode(NextRepeatNode *node){
-		ImGui::Text("next repeat");
+	virtual bool visitNextRepeatNode(NextRepeatNode *node) {
+		ImGui::TextColored(_state->_colors._keyword_color, "next repeat");
 		return true;
 	}
 
-	virtual bool visitExitRepeatNode(ExitRepeatNode *node){
-		ImGui::Text("exit repeat");
+	virtual bool visitExitRepeatNode(ExitRepeatNode *node) {
+		ImGui::TextColored(_state->_colors._keyword_color, "exit repeat");
 		return true;
 	}
 
-	virtual bool visitExitNode(ExitNode *node){
-		ImGui::Text("exit");
+	virtual bool visitExitNode(ExitNode *node) {
+		ImGui::TextColored(_state->_colors._keyword_color, "exit");
 		return true;
 	}
 
-	virtual bool visitReturnNode(ReturnNode *node){
-		ImGui::Text("return");
-		if(node->expr) {
+	virtual bool visitReturnNode(ReturnNode *node) {
+		ImGui::TextColored(_state->_colors._keyword_color, "return");
+		if (node->expr) {
 			ImGui::Text(" ");
 			ImGui::SameLine();
 			node->expr->accept(this);
@@ -822,81 +841,87 @@ public:
 		return true;
 	}
 
-	virtual bool visitTellNode(TellNode *node){
-		ImGui::Text("tell ");
+	virtual bool visitTellNode(TellNode *node) {
+		ImGui::TextColored(_state->_colors._keyword_color, "tell ");
 		node->target->accept(this);
-		if(node->stmts->size() == 1) {
+		if (node->stmts->size() == 1) {
 			ImGui::SameLine();
-			ImGui::Text(" to ");
+			ImGui::TextColored(_state->_colors._keyword_color, " to ");
 			ImGui::SameLine();
 			(*node->stmts)[0]->accept(this);
 		} else {
 			indent();
 			for (uint i = 0; i < node->stmts->size(); i++) {
-				renderIndentation();
 				Node *stmt = (*node->stmts)[i];
+				renderLine(stmt->startOffset);
 				stmt->accept(this);
 				ImGui::NewLine();
 			}
 			unindent();
-			renderIndentation();
-			ImGui::Text("endtell");
+			renderLine(node->endOffset);
+			ImGui::TextColored(_state->_colors._keyword_color, "endtell");
 		}
 		return true;
 	}
 
-	virtual bool visitWhenNode(WhenNode *node){
-		ImGui::Text("when %s then %s", node->event->c_str(), node->code->c_str());
+	virtual bool visitWhenNode(WhenNode *node) {
+		ImGui::TextColored(_state->_colors._keyword_color, "when ");
+		ImGui::SameLine();
+		ImGui::Text("%s", node->event->c_str());
+		ImGui::SameLine();
+		ImGui::TextColored(_state->_colors._keyword_color, " then ");
+		ImGui::SameLine();
+		ImGui::Text("%s", node->code->c_str());
 		ImGui::SameLine();
 		return true;
 	}
 
-	virtual bool visitDeleteNode(DeleteNode *node){
-		ImGui::Text("delete ");
+	virtual bool visitDeleteNode(DeleteNode *node) {
+		ImGui::TextColored(_state->_colors._keyword_color, "delete ");
 		ImGui::SameLine();
 		node->chunk->accept(this);
 		return true;
 	}
 
-	virtual bool visitHiliteNode(HiliteNode *node){
-		ImGui::Text("hilite ");
+	virtual bool visitHiliteNode(HiliteNode *node) {
+		ImGui::TextColored(_state->_colors._keyword_color, "hilite ");
 		ImGui::SameLine();
 		node->chunk->accept(this);
 		return true;
 	}
 
-	virtual bool visitAssertErrorNode(AssertErrorNode *node){
-		ImGui::Text("scummvmAssertError ");
+	virtual bool visitAssertErrorNode(AssertErrorNode *node) {
+		ImGui::TextColored(_state->_colors._keyword_color, "scummvmAssertError ");
 		ImGui::SameLine();
 		node->stmt->accept(this);
 		return true;
 	}
 
-	virtual bool visitIntNode(IntNode *node){
-		ImGui::Text("%d", node->val);
+	virtual bool visitIntNode(IntNode *node) {
+		ImGui::TextColored(_state->_colors._literal_color, "%d", node->val);
 		ImGui::SameLine();
 		return true;
 	}
 
-	virtual bool visitFloatNode(FloatNode *node){
-		ImGui::Text("%g", node->val);
+	virtual bool visitFloatNode(FloatNode *node) {
+		ImGui::TextColored(_state->_colors._literal_color, "%g", node->val);
 		ImGui::SameLine();
 		return true;
 	}
 
-	virtual bool visitSymbolNode(SymbolNode *node){
-		ImGui::Text("%s", node->val->c_str());
+	virtual bool visitSymbolNode(SymbolNode *node) {
+		ImGui::TextColored(_state->_colors._literal_color, "%s", node->val->c_str());
 		ImGui::SameLine();
 		return true;
 	}
 
-	virtual bool visitStringNode(StringNode *node){
-		ImGui::Text("\"%s\"", node->val->c_str());
+	virtual bool visitStringNode(StringNode *node) {
+		ImGui::TextColored(_state->_colors._literal_color, "\"%s\"", node->val->c_str());
 		ImGui::SameLine();
 		return true;
 	}
 
-	virtual bool visitListNode(ListNode *node){
+	virtual bool visitListNode(ListNode *node) {
 		ImGui::Text("[");
 		ImGui::SameLine();
 		for (uint i = 0; i < node->items->size(); i++) {
@@ -912,10 +937,10 @@ public:
 		return true;
 	}
 
-	virtual bool visitPropListNode(PropListNode *node){
+	virtual bool visitPropListNode(PropListNode *node) {
 		ImGui::Text("[");
 		ImGui::SameLine();
-		if(node->items->empty()) {
+		if (node->items->empty()) {
 			ImGui::Text(":");
 			ImGui::SameLine();
 		} else {
@@ -933,7 +958,7 @@ public:
 		return true;
 	}
 
-	virtual bool visitPropPairNode(PropPairNode *node){
+	virtual bool visitPropPairNode(PropPairNode *node) {
 		node->key->accept(this);
 		ImGui::Text(":");
 		ImGui::SameLine();
@@ -941,8 +966,28 @@ public:
 		return true;
 	}
 
-	virtual bool visitFuncNode(FuncNode *node){
-		ImGui::Text("%s(", node->name->c_str());
+	virtual bool visitFuncNode(FuncNode *node) {
+		const bool isBuiltin = g_lingo->_builtinCmds.contains(*node->name);
+		const ImVec4 color = (ImVec4)ImColor(isBuiltin ? _state->_colors._builtin_color : _state->_colors._call_color);
+		ImGui::TextColored(color, "%s(", node->name->c_str());
+		if (!isBuiltin && ImGui::IsItemHovered() && ImGui::BeginTooltip()) {
+			ImGui::Text("Go to definition");
+			ImGui::EndTooltip();
+		}
+		if (!isBuiltin && ImGui::IsItemClicked()) {
+			int obj = 0;
+			for (uint i = 0; i < _script.bytecodeArray.size(); i++) {
+				if (node->startOffset == _script.bytecodeArray[i].pos) {
+					obj = _script.bytecodeArray[i].obj;
+					break;
+				}
+			}
+
+			ImGuiScript script = toImGuiScript(_script.type, CastMemberID(obj, _script.id.castLib), *node->name);
+			script.moviePath = _script.moviePath;
+			script.handlerName = *node->name;
+			setScriptToDisplay(script);
+		}
 		ImGui::SameLine();
 		for (uint i = 0; i < node->args->size(); i++) {
 			Node *arg = (*node->args)[i];
@@ -957,13 +1002,23 @@ public:
 		return true;
 	}
 
-	virtual bool visitVarNode(VarNode *node){
-		ImGui::Text("%s", node->name->c_str());
+	virtual bool visitVarNode(VarNode *node) {
+		ImGui::TextColored(_state->_colors._var_color, "%s", node->name->c_str());
+		if (ImGui::IsItemHovered() && g_lingo->_globalvars.contains(*node->name)) {
+			const Datum &val = g_lingo->_globalvars.getVal(*node->name);
+			ImGui::BeginTooltip();
+			ImGui::Text("Click to add to watches.");
+			ImGui::Text("= %s", val.asString(true).c_str());
+			ImGui::EndTooltip();
+		}
+		if (ImGui::IsItemClicked()) {
+			_state->_variables[*node->name] = true;
+		}
 		ImGui::SameLine();
 		return true;
 	}
 
-	virtual bool visitParensNode(ParensNode *node){
+	virtual bool visitParensNode(ParensNode *node) {
 		ImGui::Text("(");
 		ImGui::SameLine();
 		node->expr->accept(this);
@@ -972,11 +1027,11 @@ public:
 		return true;
 	}
 
-	virtual bool visitUnaryOpNode(UnaryOpNode *node){
+	virtual bool visitUnaryOpNode(UnaryOpNode *node) {
 		char op = '?';
-		if(node->op == LC::c_negate) {
+		if (node->op == LC::c_negate) {
 			op = '-';
-		} else if(node->op == LC::c_not) {
+		} else if (node->op == LC::c_not) {
 			op = '!';
 		}
 		ImGui::Text("%c", op);
@@ -985,7 +1040,7 @@ public:
 		return true;
 	}
 
-	virtual bool visitBinaryOpNode(BinaryOpNode *node){
+	virtual bool visitBinaryOpNode(BinaryOpNode *node) {
 		node->a->accept(this);
 		static struct {
 			inst op;
@@ -1018,143 +1073,143 @@ public:
 		}
 		node->b->accept(this);
 		return true;
-	 }
+	}
 
-	virtual bool visitFrameNode(FrameNode *node){
-		ImGui::Text("frame ");
+	virtual bool visitFrameNode(FrameNode *node) {
+		ImGui::TextColored(_state->_colors._keyword_color, "frame ");
 		ImGui::SameLine();
 		node->arg->accept(this);
 		return true;
 	}
 
-	virtual bool visitMovieNode(MovieNode *node){
-		ImGui::Text("movie ");
+	virtual bool visitMovieNode(MovieNode *node) {
+		ImGui::TextColored(_state->_colors._keyword_color, "movie ");
 		ImGui::SameLine();
 		node->arg->accept(this);
 		return true;
 	}
 
-	virtual bool visitIntersectsNode(IntersectsNode *node){
-		ImGui::Text("sprite ");
+	virtual bool visitIntersectsNode(IntersectsNode *node) {
+		ImGui::TextColored(_state->_colors._keyword_color, "sprite ");
 		ImGui::SameLine();
 		node->sprite1->accept(this);
-		ImGui::Text("intersects ");
+		ImGui::TextColored(_state->_colors._keyword_color, "intersects ");
 		node->sprite2->accept(this);
 		return true;
 	}
 
-	virtual bool visitWithinNode(WithinNode *node){
-		ImGui::Text("sprite ");
+	virtual bool visitWithinNode(WithinNode *node) {
+		ImGui::TextColored(_state->_colors._keyword_color, "sprite ");
 		ImGui::SameLine();
 		node->sprite1->accept(this);
-		ImGui::Text("within ");
+		ImGui::TextColored(_state->_colors._keyword_color, "within ");
 		node->sprite2->accept(this);
 		return true;
 	}
 
-	virtual bool visitTheNode(TheNode *node){
-		ImGui::Text("the %s", node->prop->c_str());
+	virtual bool visitTheNode(TheNode *node) {
+		ImGui::TextColored(_state->_colors._the_color, "the %s", node->prop->c_str());
 		ImGui::SameLine();
 		return true;
 	}
 
-	virtual bool visitTheOfNode(TheOfNode *node){
-		ImGui::Text("the %s of ", node->prop->c_str());
+	virtual bool visitTheOfNode(TheOfNode *node) {
+		ImGui::TextColored(_state->_colors._the_color, "the %s of ", node->prop->c_str());
 		ImGui::SameLine();
 		node->obj->accept(this);
 		return true;
 	}
 
-	virtual bool visitTheNumberOfNode(TheNumberOfNode *node){
-		ImGui::Text("the number of ");
+	virtual bool visitTheNumberOfNode(TheNumberOfNode *node) {
+		ImGui::TextColored(_state->_colors._the_color, "the number of ");
 		ImGui::SameLine();
 		node->arg->accept(this);
 		return true;
 	}
 
-	virtual bool visitTheLastNode(TheLastNode *node){
+	virtual bool visitTheLastNode(TheLastNode *node) {
 		// TODO: change the node to know if it's 'in' or 'of'
-		ImGui::Text("the last %s in/of ", toString(node->type).c_str());
+		ImGui::TextColored(_state->_colors._the_color, "the last %s in/of ", toString(node->type).c_str());
 		ImGui::SameLine();
 		node->arg->accept(this);
 		return true;
 	}
 
-	virtual bool visitTheDateTimeNode(TheDateTimeNode *node){
-		const char* key1 = "";
-		switch(node->field) {
-			case kTheAbbr:
+	virtual bool visitTheDateTimeNode(TheDateTimeNode *node) {
+		const char *key1 = "";
+		switch (node->field) {
+		case kTheAbbr:
 			key1 = "abbreviated";
 			break;
-			case kTheLong:
+		case kTheLong:
 			key1 = "long";
 			break;
-			case kTheShort:
+		case kTheShort:
 			key1 = "short";
 			break;
 		}
 		const char *key2 = node->entity == kTheDate ? "date" : "time";
-		ImGui::Text("the %s %s", key1, key2);
+		ImGui::TextColored(_state->_colors._the_color, "the %s %s", key1, key2);
 		ImGui::SameLine();
 		return true;
 	}
 
-	virtual bool visitMenuNode(MenuNode *node){
-		ImGui::Text("menu ");
+	virtual bool visitMenuNode(MenuNode *node) {
+		ImGui::TextColored(_state->_colors._keyword_color, "menu ");
 		ImGui::SameLine();
 		node->arg->accept(this);
 		return true;
 	}
 
-	virtual bool visitMenuItemNode(MenuItemNode *node){
-		ImGui::Text("menuitem ");
+	virtual bool visitMenuItemNode(MenuItemNode *node) {
+		ImGui::TextColored(_state->_colors._keyword_color, "menuitem ");
 		ImGui::SameLine();
 		node->arg1->accept(this);
-		ImGui::Text("of menu ");
+		ImGui::TextColored(_state->_colors._keyword_color, "of menu ");
 		ImGui::SameLine();
 		node->arg2->accept(this);
 		return true;
 	}
 
-	virtual bool visitSoundNode(SoundNode *node){
-		ImGui::Text("sound ");
+	virtual bool visitSoundNode(SoundNode *node) {
+		ImGui::TextColored(_state->_colors._keyword_color, "sound ");
 		ImGui::SameLine();
 		node->arg->accept(this);
 		return true;
 	}
 
-	virtual bool visitSpriteNode(SpriteNode *node){
-		ImGui::Text("sprite ");
+	virtual bool visitSpriteNode(SpriteNode *node) {
+		ImGui::TextColored(_state->_colors._keyword_color, "sprite ");
 		ImGui::SameLine();
 		node->arg->accept(this);
 		return true;
 	}
 
-	virtual bool visitChunkExprNode(ChunkExprNode *node){
-		const char* key1 = "";
-		switch(node->type) {
-			case kChunkChar:
+	virtual bool visitChunkExprNode(ChunkExprNode *node) {
+		const char *key1 = "";
+		switch (node->type) {
+		case kChunkChar:
 			key1 = "char";
 			break;
-			case kChunkWord:
+		case kChunkWord:
 			key1 = "word";
 			break;
-			case kChunkItem:
+		case kChunkItem:
 			key1 = "item";
 			break;
-			case kChunkLine:
+		case kChunkLine:
 			key1 = "line";
 			break;
 		}
 		ImGui::Text("%s", key1);
 		ImGui::SameLine();
 		node->start->accept(this);
-		if(node->end) {
-			ImGui::Text(" to ");
+		if (node->end) {
+			ImGui::TextColored(_state->_colors._keyword_color, " to ");
 			ImGui::SameLine();
 			node->end->accept(this);
 		}
-		ImGui::Text(" of ");
+		ImGui::TextColored(_state->_colors._keyword_color, " of ");
 		ImGui::SameLine();
 		node->src->accept(this);
 		return true;
@@ -1163,14 +1218,14 @@ public:
 private:
 	static Common::String toString(ChunkType chunkType) {
 		// TODO: this method could be used in ChunkExprNode
-		switch(chunkType) {
-			case kChunkChar:
+		switch (chunkType) {
+		case kChunkChar:
 			return "char";
-			case kChunkWord:
+		case kChunkWord:
 			return "word";
-			case kChunkItem:
+		case kChunkItem:
 			return "item";
-			case kChunkLine:
+		case kChunkLine:
 			return "line";
 		}
 		return "<unknown>";
@@ -1191,7 +1246,81 @@ private:
 			ImGui::SameLine();
 		}
 	}
+
+	void renderLine(uint32 pc) {
+		bool showCurrentStatement = false;
+		_script.startOffsets.push_back(pc);
+
+		if (_isScriptInDebug && g_lingo->_exec._state == kPause) {
+			// check current statement
+			if (!_currentStatementDisplayed) {
+				if (g_lingo->_state->pc <= pc) {
+					showCurrentStatement = true;
+					_currentStatementDisplayed = true;
+				}
+			}
+		}
+
+		ImDrawList *dl = ImGui::GetWindowDrawList();
+		const ImVec2 pos = ImGui::GetCursorScreenPos();
+		const float width = ImGui::GetContentRegionAvail().x;
+		const ImVec2 mid(pos.x + 7, pos.y + 7);
+
+		ImVec4 color = _state->_colors._bp_color_disabled;
+		const Director::Breakpoint *bp = getBreakpoint(_script.handlerId, _script.id.member, pc);
+		if (bp)
+			color = _state->_colors._bp_color_enabled;
+
+		ImGui::InvisibleButton("Line", ImVec2(16, ImGui::GetFontSize()));
+
+		// click on breakpoint column?
+		if (ImGui::IsItemClicked(0)) {
+			if (color == _state->_colors._bp_color_enabled) {
+				g_lingo->delBreakpoint(bp->id);
+				color = _state->_colors._bp_color_disabled;
+			} else {
+				Director::Breakpoint newBp;
+				newBp.type = kBreakpointFunction;
+				newBp.scriptId = _script.id.member;
+				newBp.funcName = _script.handlerId;
+				newBp.funcOffset = pc;
+				g_lingo->addBreakpoint(newBp);
+				color = _state->_colors._bp_color_enabled;
+			}
+		}
+
+		if (color == _state->_colors._bp_color_disabled && ImGui::IsItemHovered()) {
+			color = _state->_colors._bp_color_hover;
+		}
+
+		// draw breakpoint
+		if (!bp || bp->enabled)
+			dl->AddCircleFilled(mid, 4.0f, ImColor(color));
+		else
+			dl->AddCircle(mid, 4.0f, ImColor(_state->_colors._line_color));
+
+		// draw current statement
+		if (showCurrentStatement) {
+			dl->AddQuadFilled(ImVec2(pos.x, pos.y + 4.f), ImVec2(pos.x + 9.f, pos.y + 4.f), ImVec2(pos.x + 9.f, pos.y + 10.f), ImVec2(pos.x, pos.y + 10.f), ImColor(_state->_colors._current_statement));
+			dl->AddTriangleFilled(ImVec2(pos.x + 8.f, pos.y), ImVec2(pos.x + 14.f, pos.y + 7.f), ImVec2(pos.x + 8.f, pos.y + 14.f), ImColor(_state->_colors._current_statement));
+			if (_state->_dbg._isScriptDirty && !ImGui::IsItemVisible()) {
+				ImGui::SetScrollHereY(0.5f);
+			}
+			dl->AddRectFilled(ImVec2(pos.x + 16.f, pos.y), ImVec2(pos.x + width, pos.y + 16.f), ImColor(IM_COL32(0xFF, 0xFF, 0x00, 0x20)), 0.4f);
+		}
+		// draw separator
+		dl->AddLine(ImVec2(pos.x + 16.0f, pos.y), ImVec2(pos.x + 16.0f, pos.y + 17), ImColor(_state->_colors._line_color));
+
+		ImGui::SetItemTooltip("Click to add a breakpoint");
+		ImGui::SameLine();
+
+		// draw offset
+		ImGui::Text("[%5d] ", pc == 0xFFFFFFFF ? -1 : pc);
+		ImGui::SameLine();
+		renderIndentation();
+	}
 };
+
 class RenderScriptVisitor : public LingoDec::NodeVisitor {
 public:
 	RenderScriptVisitor(ImGuiScript &script, bool showByteCode) : _script(script), _showByteCode(showByteCode) {
@@ -2132,7 +2261,7 @@ private:
 					ImGui::Text(", ");
 					ImGui::SameLine();
 				}
-				ImGui::TextColored(_state->_colors._var_color, "%s", _script.argumentNames[i].c_str());
+				ImGui::Text("%s", _script.argumentNames[i].c_str());
 				ImGui::SameLine();
 			}
 		}
@@ -2367,6 +2496,8 @@ private:
 
 static uint32 getLineFromPC() {
 	const uint pc = g_lingo->_state->pc;
+	if (_state->_functions._scripts.empty())
+		return 0;
 	const Common::Array<uint> &offsets = _state->_functions._scripts[_state->_functions._current].startOffsets;
 	for (uint i = 0; i < offsets.size(); i++) {
 		if (pc <= offsets[i])
@@ -3446,16 +3577,18 @@ static void displayScripts() {
 			}
 			ImGui::EndCombo();
 		}
-		ImGui::SameLine(0, 20);
 
-		toggleButton("\uf569", &_state->_functions._showByteCode, true); // Lingo		// package_2
-		ImGui::SetItemTooltip("Lingo");
-		ImGui::SameLine();
+		if (!_state->_functions._scripts[_state->_functions._current].oldAst) {
+			ImGui::SameLine(0, 20);
+			toggleButton("\uf569", &_state->_functions._showByteCode, true); // Lingo		// package_2
+			ImGui::SetItemTooltip("Lingo");
+			ImGui::SameLine();
 
-		toggleButton("\uf500", &_state->_functions._showByteCode); // Bytecode	// stacks
-		ImGui::SetItemTooltip("Bytecode");
-		ImGui::Separator();
+			toggleButton("\uf500", &_state->_functions._showByteCode); // Bytecode	// stacks
+			ImGui::SetItemTooltip("Bytecode");
+		}
 
+		ImGui::Separator();
 		const ImVec2 childsize = ImGui::GetContentRegionAvail();
 		ImGui::BeginChild("##script", childsize);
 		ImGuiScript &script = _state->_functions._scripts[_state->_functions._current];




More information about the Scummvm-git-logs mailing list