Browse Source

Work on jumping to the the editor and line as you step through the code with the debugger. Prep for moving the duktape debugger ui into it’s own panel so that pause, step over, step into, and step out bring up the proper editors.

Shaddock Heath 8 years ago
parent
commit
bda9cb6bbc

+ 1 - 0
Script/AtomicEditor/editor/EditorEvents.ts

@@ -26,3 +26,4 @@ export const IPCPlayerExitRequestEventType: Atomic.EventType = "IPCPlayerExitReq
 export const IPCPlayerWindowChangedEventType: Atomic.EventType = "IPCPlayerWindowChanged";
 export const IPCPlayerPauseResumeRequestEventType: Atomic.EventType = "IPCPlayerPauseResumeRequest";
 export const IPCPlayerPauseStepRequestEventType: Atomic.EventType = "IPCPlayerPauseStepRequest";
+export const IPCPlayerUpdatesPausedResumedEventType: Atomic.EventType = "IPCPlayerUpdatesPausedResumed";

+ 87 - 7
Script/AtomicEditor/hostExtensions/coreExtensions/DuktapeDebuggerExtension.ts

@@ -19,6 +19,7 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 //
+import EditorEvents = require("../../editor/EditorEvents");
 
 export interface Breakpoint {
     fileName: string;
@@ -87,6 +88,10 @@ interface ClientProxyMappings {
     toggleBreakpoint: string;
     addBreakpoint: string;
     removeBreakpoint: string;
+    pause: string;
+    step: string;
+    resume: string;
+    stop: string;
 }
 
 interface ClientListener {
@@ -95,6 +100,8 @@ interface ClientListener {
     callbacks: ClientProxyMappings;
 }
 
+const DUKTAPE_DEBUGGER_NAME = "Duktape Debugger";
+
 /**
  * extension that will communicate with the duktape debugger
  */
@@ -117,6 +124,29 @@ export default class DuktapeDebuggerExtension extends Atomic.ScriptObject implem
         serviceLocator.projectServices.register(this);
         serviceLocator.uiServices.register(this);
         this.serviceRegistry = serviceLocator;
+
+        this.subscribeToEvent(Editor.EditorResourceCloseEvent((data) => this.handleResourceClosedEvent(data)));
+        this.subscribeToEvent(Editor.EditorPlayerPausedEvent((data) => this.handlePause()));
+        this.subscribeToEvent(Editor.EditorPlayerResumedEvent((data) => this.handleResume()));
+        this.subscribeToEvent(Editor.EditorPlayerStoppedEvent((data) => this.handleStop()));
+        //this.subscribeToEvent(Atomic.PauseStepRequestedEvent((data) => console.log("step")));
+        //this.subscribeToEvent(Atomic.PauseResumeRequestedEvent((data) => console.log("step resume")));
+
+
+        //this.subscribeToEvent(Atomic.ScriptEvent(EditorEvents.IPCPlayerPauseResumeRequestEventType, (data) => console.log("step resume")));
+        this.subscribeToEvent(Atomic.ScriptEvent(EditorEvents.IPCPlayerPauseStepRequestEventType, (data) => this.handleStep()));
+
+
+    }
+
+    handleResourceClosedEvent(data: Editor.EditorResourceCloseEvent) {
+        // Remove the editor we are listening to
+        this.listeners = this.listeners.filter(l => {
+            if (data.editor.fullPath.lastIndexOf(l.name) == data.editor.fullPath.length - l.name.length) {
+                return false;
+            }
+            return true;
+        });
     }
 
     /**
@@ -127,11 +157,11 @@ export default class DuktapeDebuggerExtension extends Atomic.ScriptObject implem
     handleWebMessage(webMessage: WebView.WebMessageEvent, messageType: string, data: any) {
         switch (messageType) {
             case "Debugger.AddBreakpoint":
-                this.addBreakpoint(data);
+                this.addBreakpoint(data, webMessage.handler.webClient);
                 this.webMessageEventResponse(webMessage);
                 break;
             case "Debugger.RemoveBreakpoint":
-                this.removeBreakpoint(data);
+                this.removeBreakpoint(data, webMessage.handler.webClient);
                 this.webMessageEventResponse(webMessage);
                 break;
             case "Debugger.ToggleBreakpoint":
@@ -149,6 +179,10 @@ export default class DuktapeDebuggerExtension extends Atomic.ScriptObject implem
                 this.registerDebuggerListener(webMessage, data);
                 this.webMessageEventResponse(webMessage);
                 break;
+            case "Debugger.CurrentSourcePosition":
+                this.setCurrentSourcePosition(data);
+                this.webMessageEventResponse(webMessage);
+                break;
         }
     }
 
@@ -181,10 +215,10 @@ export default class DuktapeDebuggerExtension extends Atomic.ScriptObject implem
         this.listeners = this.listeners.filter(l => l.frame);
     }
 
-    addBreakpoint(bp: Breakpoint) {
+    addBreakpoint(bp: Breakpoint, sender: WebView.WebClient) {
         this.breakpointList.addBreakpoint(bp.fileName, bp.lineNumber);
         for (let listener of this.listeners) {
-            if (listener.frame) {
+            if (listener.frame && listener.frame != sender) {
                 console.log(`Adding breakpoint ${bp.fileName}:${bp.lineNumber} to ${listener.name}`);
                 this.proxyWebClientMethod(
                     listener.frame,
@@ -196,10 +230,10 @@ export default class DuktapeDebuggerExtension extends Atomic.ScriptObject implem
         }
     }
 
-    removeBreakpoint(bp: Breakpoint) {
+    removeBreakpoint(bp: Breakpoint, sender: WebView.WebClient) {
         this.breakpointList.removeBreakpoint(bp.fileName, bp.lineNumber);
         for (let listener of this.listeners) {
-            if (listener.frame) {
+            if (listener.frame && listener.frame != sender) {
                 console.log(`Remove breakpoint ${bp.fileName}:${bp.lineNumber} to ${listener.name}`);
                 this.proxyWebClientMethod(
                     listener.frame,
@@ -226,9 +260,48 @@ export default class DuktapeDebuggerExtension extends Atomic.ScriptObject implem
         }
     }
 
+    handleResume() {
+        for (let listener of this.listeners) {
+            if (listener.frame && listener.name == DUKTAPE_DEBUGGER_NAME) {
+                console.log(`Sending Pause to ${listener.name}`);
+                this.proxyWebClientMethod(
+                    listener.frame,
+                    listener.callbacks.resume);
+            }
+        }
+    }
+
+    handlePause() {
+        for (let listener of this.listeners) {
+            if (listener.frame && listener.name == DUKTAPE_DEBUGGER_NAME) {
+                console.log(`Sending Pause to ${listener.name}`);
+                this.proxyWebClientMethod(
+                    listener.frame,
+                    listener.callbacks.pause);
+            }
+        }
+    }
 
-    resume() {
+    handleStep() {
+        for (let listener of this.listeners) {
+            if (listener.frame && listener.name == DUKTAPE_DEBUGGER_NAME) {
+                console.log(`Sending Pause to ${listener.name}`);
+                this.proxyWebClientMethod(
+                    listener.frame,
+                    listener.callbacks.step);
+            }
+        }
+    }
 
+    handleStop() {
+        for (let listener of this.listeners) {
+            if (listener.frame && listener.name == DUKTAPE_DEBUGGER_NAME) {
+                console.log(`Sending Pause to ${listener.name}`);
+                this.proxyWebClientMethod(
+                    listener.frame,
+                    listener.callbacks.stop);
+            }
+        }
     }
 
     attach() {
@@ -239,6 +312,13 @@ export default class DuktapeDebuggerExtension extends Atomic.ScriptObject implem
 
     }
 
+    setCurrentSourcePosition(bp: Breakpoint) {
+        console.log(`Trying to open: ${bp.fileName}:${bp.lineNumber}`);
+        this.sendEvent(Editor.EditorEditResourceEventData({
+            path: bp.fileName,
+            lineNumber: bp.lineNumber
+        }));
+    }
     /**
      * Utility function that will compose a method call to the web client and execute it
      * It will construct it in the form of "MethodName(....);"

+ 1 - 2
Script/AtomicEditor/ui/frames/ResourceFrame.ts

@@ -144,12 +144,11 @@ class ResourceFrame extends ScriptWidget {
     }
 
     /**
-     * 
      * Set the cursor to the correct line number in the editor
      * @param {Editor.ResourceEditor} editor
      * @param {any} [lineNumber=-1]
      * @param {any} [tokenPos=-1]
-     * 
+     *
      * @memberOf ResourceFrame
      */
     setCursorPositionInEditor(editor: Editor.ResourceEditor, lineNumber = -1, tokenPos = -1) {

+ 37 - 41
Script/AtomicWebViewEditor/debugger/DuktapeDebugger.ts

@@ -134,6 +134,7 @@ export default class DuktapeDebugger {
 
         this.editor.revealLineInCenterIfOutsideViewport(reqLine);
         this.editor.setPosition(new monaco.Position(reqLine, 0));
+        debuggerProxy.notifyHostCurrentSourcePosition("Resources/" + this.loadedFileName, reqLine);
     }
 
     /*
@@ -289,6 +290,32 @@ export default class DuktapeDebugger {
         }
     }
 
+    pause() {
+        this.socket.emit("pause", {});
+
+        // Pause may take seconds to complete so indicate it is pending.
+        $("#pause-button").addClass("pending");
+    }
+
+    resume() {
+        this.socket.emit("resume", {});
+    }
+
+    stepInto() {
+        this.socket.emit("stepinto", {});
+        setTimeout(() => this.doSourceUpdate(), 125);
+    }
+
+    stepOut() {
+        this.socket.emit("stepout", {});
+        setTimeout(() => this.doSourceUpdate(), 125);
+    }
+
+    stepOver() {
+        this.socket.emit("stepover", {});
+        setTimeout(() => this.doSourceUpdate(), 125);
+    }
+
     initSocket() {
         /*
          *  Init socket.io and add handlers
@@ -532,28 +559,11 @@ export default class DuktapeDebugger {
             this.bytecodeIdxInstr = msg.idxPreformattedInstructions;
         });
 
-        $("#stepinto-button").click(() => {
-            this.socket.emit("stepinto", {});
-        });
-
-        $("#stepover-button").click(() => {
-            this.socket.emit("stepover", {});
-        });
-
-        $("#stepout-button").click(() => {
-            this.socket.emit("stepout", {});
-        });
-
-        $("#pause-button").click(() => {
-            this.socket.emit("pause", {});
-
-            // Pause may take seconds to complete so indicate it is pending.
-            $("#pause-button").addClass("pending");
-        });
-
-        $("#resume-button").click(() => {
-            this.socket.emit("resume", {});
-        });
+        $("#stepinto-button").click(() => this.stepInto());
+        $("#stepover-button").click(() => this.stepOver());
+        $("#stepout-button").click(() => this.stepOut());
+        $("#pause-button").click(() => this.pause());
+        $("#resume-button").click(() => this.resume());
 
         $("#attach-button").click(() => {
             this.socket.emit("attach", {});
@@ -619,18 +629,6 @@ export default class DuktapeDebugger {
 
 
     setSourceText(data) {
-        /* TSH
-        var elem, div;
-
-        elem = $("#source-code");
-        elem.empty();
-        data.split("\n").forEach(function (line) {
-            div = $("<div></div>");
-            div.text(line);
-            elem.append(div);
-        });
-        */
-
         this.editor.deltaDecorations([], []);
         this.editor.getModel().setValue(data);
         this.sourceEditedLines = [];
@@ -640,12 +638,6 @@ export default class DuktapeDebugger {
      *  AJAX request handling to fetch source files
      */
     requestSourceFile(fileName: string, lineNumber: number) {
-        // If previous update is pending, abort and start a new one.
-        if (this.sourceFetchXhr) {
-            this.sourceFetchXhr.abort();
-            this.sourceFetchXhr = null;
-        }
-
         console.log(`Retrieving File: ${fileName}`);
         // get the code
         return HostInteropType.getInstance().getFileResource("Resources/" + fileName).then((data: string) => {
@@ -749,7 +741,7 @@ export default class DuktapeDebugger {
 
         // Source is updated periodically.  Other code can also call doSourceUpdate()
         // directly if an immediate update is needed.
-        this.sourceUpdateInterval = setInterval(this.doSourceUpdate.bind(this), this.SOURCE_UPDATE_INTERVAL);
+        // this.sourceUpdateInterval = setInterval(this.doSourceUpdate.bind(this), this.SOURCE_UPDATE_INTERVAL);
         this.editor.onMouseMove((e) => {
             var targetZone = e.target.toString();
             if (targetZone.indexOf("GUTTER_GLYPH_MARGIN") != -1) {
@@ -856,6 +848,10 @@ export default class DuktapeDebugger {
             debuggerProxy.debuggerHostKeys.removeBreakpoint,
             this.removeBreakpoint.bind(this));
 
+        interop.addCustomHostRoutine(
+            debuggerProxy.debuggerHostKeys.pause,
+            this.pause.bind(this));
+
         debuggerProxy.registerDebuggerListener("DuktapeDebugger");
     }
 

+ 19 - 3
Script/AtomicWebViewEditor/debugger/HostDebuggerExtensionProxy.ts

@@ -9,7 +9,7 @@ export interface Breakpoint {
  * Get a list of all breakpoints that have been registered
  * @return {Breakpoint[]}
  */
-export async function getBreakpoints() : Promise<Breakpoint[]> {
+export async function getBreakpoints(): Promise<Breakpoint[]> {
     return atomicHostRequest<Breakpoint[]>("Debugger.GetBreakpoints");
 }
 
@@ -59,7 +59,11 @@ export async function removeAllBreakpoints() {
 export const debuggerHostKeys = {
     toggleBreakpoint: "HOST_DEBUGGER_ToggleBreakpoint",
     addBreakpoint: "HOST_DEBUGGER_AddBreakpoint",
-    removeBreakpoint: "HOST_DEBUGGER_RemoveBreakpoint"
+    removeBreakpoint: "HOST_DEBUGGER_RemoveBreakpoint",
+    pause: "HOST_DEBUGGER_Pause",
+    step: "HOST_DEBUGGER_Step",
+    resume: "HOST_DEBUGGER_Resume",
+    stop: "HOST_DEBUGGER_Stop"
 };
 
 /**
@@ -68,9 +72,21 @@ export const debuggerHostKeys = {
  * host initiated debugger messages
  * @param {string} name The name associated with the listener.  Ususally the filename.
  */
-export function  registerDebuggerListener(name: string) {
+export function registerDebuggerListener(name: string) {
     atomicHostEvent("Debugger.RegisterDebuggerListener", {
         name,
         callbacks: debuggerHostKeys
     });
 }
+
+/**
+ * Notify the host of the current line number and source file.  Used during pause, step, etc.
+ * @param  {string} fileName
+ * @param  {number} lineNumber
+ */
+export async function notifyHostCurrentSourcePosition(fileName: string, lineNumber: number) {
+    return atomicHostEvent("Debugger.CurrentSourcePosition", {
+        fileName,
+        lineNumber
+    });
+}

+ 1 - 4
Source/AtomicEditor/Editors/JSResourceEditor.cpp

@@ -131,7 +131,6 @@ void JSResourceEditor::HandleWebMessage(StringHash eventType, VariantMap& eventD
         String message = jvalue["message"].GetString();
         if (message == EDITOR_CHANGE) {
             SetModified(true);
-            handler->Success();
         }
         else if (message == EDITOR_SAVE_CODE)
         {
@@ -139,7 +138,6 @@ void JSResourceEditor::HandleWebMessage(StringHash eventType, VariantMap& eventD
             File file(context_, fullpath_, FILE_WRITE);
             file.Write((void*) code.CString(), code.Length());
             file.Close();
-            handler->Success();
         }
         else if (message == EDITOR_SAVE_FILE)
         {
@@ -160,11 +158,10 @@ void JSResourceEditor::HandleWebMessage(StringHash eventType, VariantMap& eventD
             } else {
                 ATOMIC_LOGWARNING("Ignoring attempt to write file: " + fn);
             }
-            handler->Success();
         }
     }
 
-
+    handler->Success();
 }
 
 void JSResourceEditor::FormatCode()