[gnome-builder/wip/chergert/debugger] debugger: track debugger location in debugger view



commit d1875f5038b4c9a52f6921fb1db1ff049de08551
Author: Christian Hergert <chergert redhat com>
Date:   Mon Apr 10 12:53:03 2017 -0700

    debugger: track debugger location in debugger view
    
    This still needs lots of work, but it does some really basic plumbing that
    we can iterate on from here.

 libide/Makefile.am                             |    2 +
 libide/debugger/ide-breakpoint.c               |  149 +++++++++++++++-
 libide/debugger/ide-breakpoint.h               |   35 +++--
 libide/debugger/ide-debug-manager.c            |   14 +-
 libide/debugger/ide-debugger-gutter-renderer.c |    2 +-
 libide/debugger/ide-debugger-gutter-renderer.h |    7 +-
 libide/debugger/ide-debugger-perspective.c     |  112 +++++++++++-
 libide/debugger/ide-debugger-perspective.h     |    8 +-
 libide/debugger/ide-debugger-view.c            |  185 ++++++++++++++++++
 libide/debugger/ide-debugger-view.h            |   39 ++++
 libide/debugger/ide-debugger-view.ui           |   20 ++
 libide/debugger/ide-debugger-workbench-addin.c |   97 +----------
 libide/debugger/ide-debugger.c                 |  240 +++++++++++++++++++++++-
 libide/debugger/ide-debugger.h                 |   70 +++++---
 libide/ide-types.h                             |    1 +
 libide/ide.h                                   |    1 +
 libide/resources/libide.gresource.xml          |    1 +
 plugins/gdb/gbp-gdb-debugger.c                 |   35 ++--
 po/POTFILES.in                                 |    1 +
 19 files changed, 852 insertions(+), 167 deletions(-)
---
diff --git a/libide/Makefile.am b/libide/Makefile.am
index 2d24811..8dc9ee0 100644
--- a/libide/Makefile.am
+++ b/libide/Makefile.am
@@ -57,6 +57,7 @@ libide_1_0_la_public_headers =                                              \
        debugger/ide-debugger-perspective.h                                 \
        debugger/ide-debugger-workbench-addin.h                             \
        debugger/ide-debugger-gutter-renderer.h                             \
+       debugger/ide-debugger-view.h                                        \
        devices/ide-device-manager.h                                        \
        devices/ide-device-provider.h                                       \
        devices/ide-device.h                                                \
@@ -246,6 +247,7 @@ libide_1_0_la_public_sources =                                              \
        debugger/ide-debugger-perspective.c                                 \
        debugger/ide-debugger-workbench-addin.c                             \
        debugger/ide-debugger-gutter-renderer.c                             \
+       debugger/ide-debugger-view.c                                        \
        devices/ide-device-manager.c                                        \
        devices/ide-device-provider.c                                       \
        devices/ide-device.c                                                \
diff --git a/libide/debugger/ide-breakpoint.c b/libide/debugger/ide-breakpoint.c
index adaaa54..0b9a9de 100644
--- a/libide/debugger/ide-breakpoint.c
+++ b/libide/debugger/ide-breakpoint.c
@@ -23,17 +23,23 @@
 typedef struct
 {
   gchar *id;
+  gchar *address;
   GFile *file;
   guint  line;
+  guint  line_offset;
   guint  enabled : 1;
+  guint  transient : 1;
 } IdeBreakpointPrivate;
 
 enum {
   PROP_0,
+  PROP_ADDRESS,
   PROP_ENABLED,
   PROP_FILE,
   PROP_ID,
   PROP_LINE,
+  PROP_LINE_OFFSET,
+  PROP_TRANSIENT,
   N_PROPS
 };
 
@@ -47,6 +53,7 @@ ide_breakpoint_finalize (GObject *object)
   IdeBreakpoint *self = (IdeBreakpoint *)object;
   IdeBreakpointPrivate *priv = ide_breakpoint_get_instance_private (self);
 
+  g_clear_pointer (&priv->address, g_free);
   g_clear_object (&priv->file);
   g_clear_pointer (&priv->id, g_free);
 
@@ -63,6 +70,10 @@ ide_breakpoint_get_property (GObject    *object,
 
   switch (prop_id)
     {
+    case PROP_ADDRESS:
+      g_value_set_string (value, ide_breakpoint_get_address (self));
+      break;
+
     case PROP_ENABLED:
       g_value_set_boolean (value, ide_breakpoint_get_enabled (self));
       break;
@@ -79,6 +90,14 @@ ide_breakpoint_get_property (GObject    *object,
       g_value_set_uint (value, ide_breakpoint_get_line (self));
       break;
 
+    case PROP_LINE_OFFSET:
+      g_value_set_uint (value, ide_breakpoint_get_line_offset (self));
+      break;
+
+    case PROP_TRANSIENT:
+      g_value_set_boolean (value, ide_breakpoint_get_transient (self));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -94,6 +113,10 @@ ide_breakpoint_set_property (GObject      *object,
 
   switch (prop_id)
     {
+    case PROP_ADDRESS:
+      ide_breakpoint_set_address (self, g_value_get_string (value));
+      break;
+
     case PROP_ENABLED:
       ide_breakpoint_set_enabled (self, g_value_get_boolean (value));
       break;
@@ -110,6 +133,14 @@ ide_breakpoint_set_property (GObject      *object,
       ide_breakpoint_set_line (self, g_value_get_uint (value));
       break;
 
+    case PROP_LINE_OFFSET:
+      ide_breakpoint_set_line_offset (self, g_value_get_uint (value));
+      break;
+
+    case PROP_TRANSIENT:
+      ide_breakpoint_set_transient (self, g_value_get_boolean (value));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -124,6 +155,28 @@ ide_breakpoint_class_init (IdeBreakpointClass *klass)
   object_class->get_property = ide_breakpoint_get_property;
   object_class->set_property = ide_breakpoint_set_property;
 
+  /**
+   * IdeBreakpoint:address:
+   *
+   * The "address" property is used to denote the position of the program
+   * counter for this breakpoint. Typically, this is only need if the debugger
+   * cannot represent the breakpoint with the #IdeBreakpoint:file and
+   * #IdeBreakpoint:line properties.
+   *
+   * The #IdeDebugger might use this address to disassemble as necessary from
+   * ide_debugger_load_source_async() to retrieve the source.
+   *
+   * The address is a string, so that architectures different from the current
+   * system may be addressed, as those may be outside of the addressable range
+   * on the debugging host.
+   */
+  properties [PROP_ADDRESS] =
+    g_param_spec_string ("address",
+                         "Address",
+                         "Address of the program counter if no source is available",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
   properties [PROP_ID] =
     g_param_spec_string ("id",
                          "Id",
@@ -145,7 +198,16 @@ ide_breakpoint_class_init (IdeBreakpointClass *klass)
                        0,
                        G_MAXUINT,
                        0,
-                       (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+                       (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_LINE_OFFSET] =
+    g_param_spec_uint ("line-offset",
+                       "Line Offset",
+                       "The line offset, starting from 0",
+                       0,
+                       G_MAXUINT,
+                       0,
+                       (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
 
   properties [PROP_ENABLED] =
     g_param_spec_boolean ("enabled",
@@ -154,6 +216,13 @@ ide_breakpoint_class_init (IdeBreakpointClass *klass)
                           FALSE,
                           (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
 
+  properties [PROP_TRANSIENT] =
+    g_param_spec_boolean ("transient",
+                          "Transient",
+                          "If the breakpoint is transient, and will go away upon continuing debugging",
+                          FALSE,
+                          (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
   g_object_class_install_properties (object_class, N_PROPS, properties);
 }
 
@@ -268,3 +337,81 @@ ide_breakpoint_set_enabled (IdeBreakpoint *self,
       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ENABLED]);
     }
 }
+
+gboolean
+ide_breakpoint_get_transient (IdeBreakpoint *self)
+{
+  IdeBreakpointPrivate *priv = ide_breakpoint_get_instance_private (self);
+
+  g_return_val_if_fail (IDE_IS_BREAKPOINT (self), FALSE);
+
+  return priv->transient;
+}
+
+void
+ide_breakpoint_set_transient (IdeBreakpoint *self,
+                              gboolean       transient)
+{
+  IdeBreakpointPrivate *priv = ide_breakpoint_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_BREAKPOINT (self));
+
+  transient = !!transient;
+
+  if (priv->transient != transient)
+    {
+      priv->transient = transient;
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TRANSIENT]);
+    }
+}
+
+guint
+ide_breakpoint_get_line_offset (IdeBreakpoint *self)
+{
+  IdeBreakpointPrivate *priv = ide_breakpoint_get_instance_private (self);
+
+  g_return_val_if_fail (IDE_IS_BREAKPOINT (self), 0);
+
+  return priv->line_offset;
+}
+
+void
+ide_breakpoint_set_line_offset (IdeBreakpoint *self,
+                                guint          line_offset)
+{
+  IdeBreakpointPrivate *priv = ide_breakpoint_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_BREAKPOINT (self));
+
+  if (priv->line_offset != line_offset)
+    {
+      priv->line_offset = line_offset;
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_LINE_OFFSET]);
+    }
+}
+
+const gchar *
+ide_breakpoint_get_address (IdeBreakpoint *self)
+{
+  IdeBreakpointPrivate *priv = ide_breakpoint_get_instance_private (self);
+
+  g_return_val_if_fail (IDE_IS_BREAKPOINT (self), NULL);
+
+  return priv->address;
+}
+
+void
+ide_breakpoint_set_address (IdeBreakpoint *self,
+                            const gchar   *address)
+{
+  IdeBreakpointPrivate *priv = ide_breakpoint_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_BREAKPOINT (self));
+
+  if (g_strcmp0 (priv->address, address) != 0)
+    {
+      g_free (priv->address);
+      priv->address = g_strdup (address);
+      g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ADDRESS]);
+    }
+}
diff --git a/libide/debugger/ide-breakpoint.h b/libide/debugger/ide-breakpoint.h
index 72cd7c2..e3636dd 100644
--- a/libide/debugger/ide-breakpoint.h
+++ b/libide/debugger/ide-breakpoint.h
@@ -43,19 +43,28 @@ struct _IdeBreakpointClass
   gpointer _reserved8;
 };
 
-IdeBreakpoint *ide_breakpoint_new         (void);
-const gchar   *ide_breakpoint_get_id      (IdeBreakpoint *self);
-void           ide_breakpoint_set_id      (IdeBreakpoint *self,
-                                           const gchar   *id);
-GFile         *ide_breakpoint_get_file    (IdeBreakpoint *self);
-void           ide_breakpoint_set_file    (IdeBreakpoint *self,
-                                           GFile         *file);
-guint          ide_breakpoint_get_line    (IdeBreakpoint *self);
-void           ide_breakpoint_set_line    (IdeBreakpoint *self,
-                                           guint          line);
-gboolean       ide_breakpoint_get_enabled (IdeBreakpoint *self);
-void           ide_breakpoint_set_enabled (IdeBreakpoint *self,
-                                           gboolean       enabled);
+IdeBreakpoint *ide_breakpoint_new             (void);
+const gchar   *ide_breakpoint_get_id          (IdeBreakpoint *self);
+void           ide_breakpoint_set_id          (IdeBreakpoint *self,
+                                               const gchar   *id);
+const gchar   *ide_breakpoint_get_address     (IdeBreakpoint *self);
+void           ide_breakpoint_set_address     (IdeBreakpoint *self,
+                                               const gchar   *address);
+GFile         *ide_breakpoint_get_file        (IdeBreakpoint *self);
+void           ide_breakpoint_set_file        (IdeBreakpoint *self,
+                                               GFile         *file);
+guint          ide_breakpoint_get_line        (IdeBreakpoint *self);
+void           ide_breakpoint_set_line        (IdeBreakpoint *self,
+                                               guint          line);
+guint          ide_breakpoint_get_line_offset (IdeBreakpoint *self);
+void           ide_breakpoint_set_line_offset (IdeBreakpoint *self,
+                                               guint          line);
+gboolean       ide_breakpoint_get_enabled     (IdeBreakpoint *self);
+void           ide_breakpoint_set_enabled     (IdeBreakpoint *self,
+                                               gboolean       enabled);
+gboolean       ide_breakpoint_get_transient   (IdeBreakpoint *self);
+void           ide_breakpoint_set_transient   (IdeBreakpoint *self,
+                                               gboolean       transient);
 
 G_END_DECLS
 
diff --git a/libide/debugger/ide-debug-manager.c b/libide/debugger/ide-debug-manager.c
index 59906f5..3bb8a11 100644
--- a/libide/debugger/ide-debug-manager.c
+++ b/libide/debugger/ide-debug-manager.c
@@ -192,12 +192,13 @@ ide_debug_manager_action_continue (GSimpleAction *action,
 static void
 ide_debug_manager_debugger_stopped (IdeDebugManager       *self,
                                     IdeDebuggerStopReason  reason,
-                                    IdeSourceLocation     *location,
+                                    IdeBreakpoint         *breakpoint,
                                     IdeDebugger           *debugger)
 {
   IDE_ENTRY;
 
   g_assert (IDE_IS_DEBUG_MANAGER (self));
+  g_assert (!breakpoint || IDE_IS_BREAKPOINT (breakpoint));
   g_assert (IDE_IS_DEBUGGER (debugger));
 
   switch (reason)
@@ -209,12 +210,19 @@ ide_debug_manager_debugger_stopped (IdeDebugManager       *self,
         ide_runner_force_quit (self->runner);
       break;
 
-    case IDE_DEBUGGER_STOP_UNDEFINED:
     case IDE_DEBUGGER_STOP_BREAKPOINT:
     case IDE_DEBUGGER_STOP_WATCHPOINT:
+    case IDE_DEBUGGER_STOP_UNDEFINED:
     case IDE_DEBUGGER_STOP_SIGNALED:
-    default:
+      if (breakpoint != NULL)
+        {
+          IDE_TRACE_MSG ("Emitting breakpoint-reached");
+          g_signal_emit (self, signals [BREAKPOINT_REACHED], 0, breakpoint);
+        }
       break;
+
+    default:
+      g_assert_not_reached ();
     }
 
   IDE_EXIT;
diff --git a/libide/debugger/ide-debugger-gutter-renderer.c b/libide/debugger/ide-debugger-gutter-renderer.c
index 6a1bb3d..052de69 100644
--- a/libide/debugger/ide-debugger-gutter-renderer.c
+++ b/libide/debugger/ide-debugger-gutter-renderer.c
@@ -156,7 +156,7 @@ ide_debugger_gutter_renderer_breakpoints_changed (IdeDebuggerGutterRenderer *sel
   gtk_source_gutter_renderer_queue_draw (GTK_SOURCE_GUTTER_RENDERER (self));
 }
 
-static void
+void
 ide_debugger_gutter_renderer_set_breakpoints (IdeDebuggerGutterRenderer *self,
                                               IdeDebuggerBreakpoints    *breakpoints)
 {
diff --git a/libide/debugger/ide-debugger-gutter-renderer.h b/libide/debugger/ide-debugger-gutter-renderer.h
index e3780ff..e14a68e 100644
--- a/libide/debugger/ide-debugger-gutter-renderer.h
+++ b/libide/debugger/ide-debugger-gutter-renderer.h
@@ -20,7 +20,8 @@
 #define IDE_DEBUGGER_GUTTER_RENDERER_H
 
 #include <gtksourceview/gtksource.h>
-#include <ide.h>
+
+#include "ide-types.h"
 
 G_BEGIN_DECLS
 
@@ -28,7 +29,9 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (IdeDebuggerGutterRenderer, ide_debugger_gutter_renderer, IDE, 
DEBUGGER_GUTTER_RENDERER, GtkSourceGutterRendererPixbuf)
 
-GtkSourceGutterRenderer *ide_debugger_gutter_renderer_new (void);
+GtkSourceGutterRenderer *ide_debugger_gutter_renderer_new             (void);
+void                     ide_debugger_gutter_renderer_set_breakpoints (IdeDebuggerGutterRenderer *self,
+                                                                       IdeDebuggerBreakpoints    
*breakpoints);
 
 G_END_DECLS
 
diff --git a/libide/debugger/ide-debugger-perspective.c b/libide/debugger/ide-debugger-perspective.c
index 992fc3e..a8b6511 100644
--- a/libide/debugger/ide-debugger-perspective.c
+++ b/libide/debugger/ide-debugger-perspective.c
@@ -21,9 +21,14 @@
 #include <egg-signal-group.h>
 #include <glib/gi18n.h>
 
+#include "ide-debug.h"
+
 #include "debugger/ide-debugger.h"
 #include "debugger/ide-debugger-perspective.h"
+#include "debugger/ide-debugger-view.h"
 #include "util/ide-pango.h"
+#include "workbench/ide-layout-grid.h"
+#include "workbench/ide-perspective.h"
 
 struct _IdeDebuggerPerspective
 {
@@ -36,6 +41,7 @@ struct _IdeDebuggerPerspective
 
   GtkTextBuffer  *log_buffer;
   GtkTextView    *log_text_view;
+  IdeLayoutGrid  *layout_grid;
 };
 
 enum {
@@ -98,10 +104,12 @@ on_debugger_log (IdeDebuggerPerspective *self,
   gtk_text_view_scroll_to_iter (self->log_text_view, &iter, 0.0, FALSE, 1.0, 1.0);
 }
 
-static void
+void
 ide_debugger_perspective_set_debugger (IdeDebuggerPerspective *self,
                                        IdeDebugger            *debugger)
 {
+  IDE_ENTRY;
+
   g_return_if_fail (IDE_IS_DEBUGGER_PERSPECTIVE (self));
   g_return_if_fail (!debugger || IDE_IS_DEBUGGER (debugger));
 
@@ -111,6 +119,8 @@ ide_debugger_perspective_set_debugger (IdeDebuggerPerspective *self,
       gtk_text_buffer_set_text (self->log_buffer, "", 0);
       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DEBUGGER]);
     }
+
+  IDE_EXIT;
 }
 
 static void
@@ -147,6 +157,24 @@ log_panel_changed_font_name (IdeDebuggerPerspective *self,
 }
 
 static void
+on_debugger_stopped (IdeDebuggerPerspective *self,
+                     IdeDebuggerStopReason   reason,
+                     IdeBreakpoint          *breakpoint,
+                     IdeDebugger            *debugger)
+{
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_DEBUGGER_PERSPECTIVE (self));
+  g_assert (!breakpoint || IDE_IS_BREAKPOINT (breakpoint));
+  g_assert (IDE_IS_DEBUGGER (debugger));
+
+  if (breakpoint != NULL)
+    ide_debugger_perspective_navigate_to_breakpoint (self, breakpoint);
+
+  IDE_EXIT;
+}
+
+static void
 ide_debugger_perspective_finalize (GObject *object)
 {
   IdeDebuggerPerspective *self = (IdeDebuggerPerspective *)object;
@@ -217,7 +245,7 @@ ide_debugger_perspective_class_init (IdeDebuggerPerspectiveClass *klass)
   g_object_class_install_properties (object_class, N_PROPS, properties);
 
   gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/builder/ui/ide-debugger-perspective.ui");
-
+  gtk_widget_class_bind_template_child (widget_class, IdeDebuggerPerspective, layout_grid);
   gtk_widget_class_bind_template_child (widget_class, IdeDebuggerPerspective, log_text_view);
   gtk_widget_class_bind_template_child (widget_class, IdeDebuggerPerspective, log_buffer);
 }
@@ -237,6 +265,12 @@ ide_debugger_perspective_init (IdeDebuggerPerspective *self)
                                    self,
                                    G_CONNECT_SWAPPED);
 
+  egg_signal_group_connect_object (self->debugger_signals,
+                                   "stopped",
+                                   G_CALLBACK (on_debugger_stopped),
+                                   self,
+                                   G_CONNECT_SWAPPED);
+
   self->log_css = gtk_css_provider_new ();
   context = gtk_widget_get_style_context (GTK_WIDGET (self->log_text_view));
   gtk_style_context_add_provider (context,
@@ -251,3 +285,77 @@ ide_debugger_perspective_init (IdeDebuggerPerspective *self)
                            G_CONNECT_SWAPPED);
   log_panel_changed_font_name (self, "font-name", self->terminal_settings);
 }
+
+static void
+ide_debugger_perspective_load_source_cb (GObject      *object,
+                                         GAsyncResult *result,
+                                         gpointer      user_data)
+{
+  IdeDebugger *debugger = (IdeDebugger *)object;
+  g_autoptr(IdeDebuggerPerspective) self = user_data;
+  g_autoptr(GtkSourceBuffer) buffer = NULL;
+  g_autoptr(GError) error = NULL;
+  IdeLayoutView *view;
+  GtkWidget *stack;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_DEBUGGER (debugger));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (IDE_IS_DEBUGGER_PERSPECTIVE (self));
+
+  buffer = ide_debugger_load_source_finish (debugger, result, &error);
+
+  if (buffer == NULL)
+    {
+      g_warning ("%s", error->message);
+      IDE_GOTO (failure);
+    }
+
+  view = g_object_new (IDE_TYPE_DEBUGGER_VIEW,
+                       "buffer", buffer,
+                       "visible", TRUE,
+                       NULL);
+
+  stack = ide_layout_grid_get_last_focus (self->layout_grid);
+  gtk_container_add (GTK_CONTAINER (stack), GTK_WIDGET (view));
+
+failure:
+  IDE_EXIT;
+}
+
+void
+ide_debugger_perspective_navigate_to_breakpoint (IdeDebuggerPerspective *self,
+                                                 IdeBreakpoint          *breakpoint)
+{
+  IDE_ENTRY;
+
+  g_return_if_fail (IDE_IS_DEBUGGER_PERSPECTIVE (self));
+  g_return_if_fail (IDE_IS_BREAKPOINT (breakpoint));
+  g_return_if_fail (IDE_IS_DEBUGGER (self->debugger));
+
+  /*
+   * To display the source for the breakpoint, first we need to discover what
+   * file contains the source. If there is no file, then we need to ask the
+   * IdeDebugger to retrieve the disassembly for us so that we can show
+   * something "useful" to the developer.
+   *
+   * If we also fail to get the disassembly for the current breakpoint, we
+   * need to load some dummy text into a buffer to denote to the developer
+   * that technically they can click forward, but the behavior is rather
+   * undefined.
+   *
+   * If the file on disk is out of date (due to changes behind the scenes) we
+   * will likely catch that with a CRC check. We will show the file, but the
+   * user will have an infobar displayed that denotes that the file is not
+   * longer in sync with the debugged executable.
+   */
+
+  ide_debugger_load_source_async (self->debugger,
+                                  breakpoint,
+                                  NULL,
+                                  ide_debugger_perspective_load_source_cb,
+                                  g_object_ref (self));
+
+  IDE_EXIT;
+}
diff --git a/libide/debugger/ide-debugger-perspective.h b/libide/debugger/ide-debugger-perspective.h
index 18ee206..c03ce73 100644
--- a/libide/debugger/ide-debugger-perspective.h
+++ b/libide/debugger/ide-debugger-perspective.h
@@ -19,7 +19,8 @@
 #ifndef IDE_DEBUGGER_PERSPECTIVE_H
 #define IDE_DEBUGGER_PERSPECTIVE_H
 
-#include <ide.h>
+#include "debugger/ide-breakpoint.h"
+#include "workbench/ide-layout.h"
 
 G_BEGIN_DECLS
 
@@ -27,6 +28,11 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (IdeDebuggerPerspective, ide_debugger_perspective, IDE, DEBUGGER_PERSPECTIVE, IdeLayout)
 
+void ide_debugger_perspective_set_debugger           (IdeDebuggerPerspective *self,
+                                                      IdeDebugger            *debugger);
+void ide_debugger_perspective_navigate_to_breakpoint (IdeDebuggerPerspective *self,
+                                                      IdeBreakpoint          *breakpoint);
+
 G_END_DECLS
 
 #endif /* IDE_DEBUGGER_PERSPECTIVE_H */
diff --git a/libide/debugger/ide-debugger-view.c b/libide/debugger/ide-debugger-view.c
new file mode 100644
index 0000000..34ad3df
--- /dev/null
+++ b/libide/debugger/ide-debugger-view.c
@@ -0,0 +1,185 @@
+/* ide-debugger-view.c
+ *
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-debugger-view"
+
+#include "ide-debug.h"
+
+#include "debugger/ide-debugger-breakpoints.h"
+#include "debugger/ide-debugger-gutter-renderer.h"
+#include "debugger/ide-debugger-view.h"
+#include "sourceview/ide-source-view.h"
+
+struct _IdeDebuggerView
+{
+  IdeLayoutView              parent_instance;
+
+  IdeSourceView             *source_view;
+  IdeDebuggerBreakpoints    *breakpoints;
+  IdeDebuggerGutterRenderer *breakpoints_gutter;
+};
+
+enum {
+  PROP_0,
+  PROP_BUFFER,
+  N_PROPS
+};
+
+G_DEFINE_TYPE (IdeDebuggerView, ide_debugger_view, IDE_TYPE_LAYOUT_VIEW)
+
+static GParamSpec *properties [N_PROPS];
+
+static gchar *
+ide_debugger_view_get_title (IdeLayoutView *view)
+{
+  IdeDebuggerView *self = (IdeDebuggerView *)view;
+  const gchar *title;
+  IdeBuffer *buffer;
+
+  g_assert (IDE_IS_DEBUGGER_VIEW (self));
+
+  buffer = IDE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (self->source_view)));
+  title = ide_buffer_get_title (buffer);
+
+  return g_strdup (title);
+}
+
+static void
+ide_debugger_view_get_property (GObject    *object,
+                                guint       prop_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
+{
+  IdeDebuggerView *self = IDE_DEBUGGER_VIEW (object);
+
+  switch (prop_id)
+    {
+    case PROP_BUFFER:
+      g_value_set_object (value, ide_debugger_view_get_buffer (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_debugger_view_set_property (GObject      *object,
+                                guint         prop_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+  IdeDebuggerView *self = IDE_DEBUGGER_VIEW (object);
+
+  switch (prop_id)
+    {
+    case PROP_BUFFER:
+      ide_debugger_view_set_buffer (self, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_debugger_view_class_init (IdeDebuggerViewClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+  IdeLayoutViewClass *view_class = IDE_LAYOUT_VIEW_CLASS (klass);
+
+  object_class->get_property = ide_debugger_view_get_property;
+  object_class->set_property = ide_debugger_view_set_property;
+
+  view_class->get_title = ide_debugger_view_get_title;
+
+  properties [PROP_BUFFER] =
+    g_param_spec_object ("buffer",
+                         "Buffer",
+                         "The buffer for the view",
+                         IDE_TYPE_BUFFER,
+                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+
+  gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/builder/ui/ide-debugger-view.ui");
+  gtk_widget_class_bind_template_child (widget_class, IdeDebuggerView, source_view);
+}
+
+static void
+ide_debugger_view_init (IdeDebuggerView *self)
+{
+  GtkSourceGutter *gutter;
+
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  gutter = gtk_source_view_get_gutter (GTK_SOURCE_VIEW (self->source_view),
+                                       GTK_TEXT_WINDOW_LEFT);
+  self->breakpoints_gutter = g_object_new (IDE_TYPE_DEBUGGER_GUTTER_RENDERER,
+                                           "visible", TRUE,
+                                           NULL);
+  gtk_source_gutter_insert (gutter, GTK_SOURCE_GUTTER_RENDERER (self->breakpoints_gutter), -100);
+}
+
+GtkWidget *
+ide_debugger_view_new (void)
+{
+  return g_object_new (IDE_TYPE_DEBUGGER_VIEW, NULL);
+}
+
+/**
+ * ide_debugger_view_get_buffer:
+ * @self: a #IdeDebuggerView
+ *
+ * Gets the buffer for the view.
+ *
+ * Returns: (transfer none): A #GtkSourceBuffer
+ */
+GtkSourceBuffer *
+ide_debugger_view_get_buffer (IdeDebuggerView *self)
+{
+  g_return_val_if_fail (IDE_IS_DEBUGGER_VIEW (self), NULL);
+
+  return GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (self->source_view)));
+}
+
+void
+ide_debugger_view_set_buffer (IdeDebuggerView *self,
+                              GtkSourceBuffer *buffer)
+{
+  g_return_if_fail (IDE_IS_DEBUGGER_VIEW (self));
+  g_return_if_fail (GTK_SOURCE_IS_BUFFER (buffer));
+
+  if (buffer != ide_debugger_view_get_buffer (self))
+    {
+      GtkSourceStyleScheme *scheme;
+      GtkSourceLanguage *language;
+
+      scheme = gtk_source_style_scheme_manager_get_scheme (gtk_source_style_scheme_manager_get_default (), 
"builder-dark");
+      gtk_source_buffer_set_style_scheme (GTK_SOURCE_BUFFER (buffer), scheme);
+
+      language = gtk_source_language_manager_get_language (gtk_source_language_manager_get_default (), "c");
+      gtk_source_buffer_set_language (GTK_SOURCE_BUFFER (buffer), language);
+
+      gtk_source_view_set_background_pattern (GTK_SOURCE_VIEW (self->source_view), 
GTK_SOURCE_BACKGROUND_PATTERN_TYPE_GRID);
+
+      gtk_text_view_set_buffer (GTK_TEXT_VIEW (self->source_view), GTK_TEXT_BUFFER (buffer));
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUFFER]);
+    }
+}
diff --git a/libide/debugger/ide-debugger-view.h b/libide/debugger/ide-debugger-view.h
new file mode 100644
index 0000000..c311e08
--- /dev/null
+++ b/libide/debugger/ide-debugger-view.h
@@ -0,0 +1,39 @@
+/* ide-debugger-view.h
+ *
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_DEBUGGER_VIEW_H
+#define IDE_DEBUGGER_VIEW_H
+
+#include <gtksourceview/gtksource.h>
+
+#include "workbench/ide-layout-view.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_DEBUGGER_VIEW (ide_debugger_view_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeDebuggerView, ide_debugger_view, IDE, DEBUGGER_VIEW, IdeLayoutView)
+
+GtkWidget       *ide_debugger_view_new        (void);
+GtkSourceBuffer *ide_debugger_view_get_buffer (IdeDebuggerView *self);
+void             ide_debugger_view_set_buffer (IdeDebuggerView *self,
+                                               GtkSourceBuffer *buffer);
+
+G_END_DECLS
+
+#endif /* IDE_DEBUGGER_VIEW_H */
diff --git a/libide/debugger/ide-debugger-view.ui b/libide/debugger/ide-debugger-view.ui
new file mode 100644
index 0000000..b5c5d43
--- /dev/null
+++ b/libide/debugger/ide-debugger-view.ui
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<interface>
+  <template class="IdeDebuggerView" parent="IdeLayoutView">
+    <child>
+      <object class="GtkScrolledWindow">
+        <property name="expand">true</property>
+        <property name="visible">true</property>
+        <child>
+          <object class="IdeSourceView" id="source_view">
+            <property name="highlight-current-line">true</property>
+            <property name="show-line-numbers">true</property>
+            <property name="monospace">true</property>
+            <property name="editable">false</property>
+            <property name="visible">true</property>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/libide/debugger/ide-debugger-workbench-addin.c b/libide/debugger/ide-debugger-workbench-addin.c
index 037c0b4..da35100 100644
--- a/libide/debugger/ide-debugger-workbench-addin.c
+++ b/libide/debugger/ide-debugger-workbench-addin.c
@@ -23,6 +23,8 @@
 #include <egg-signal-group.h>
 #include <gtksourceview/gtksource.h>
 
+#include "ide-debug.h"
+
 #include "debugger/ide-breakpoint.h"
 #include "debugger/ide-debug-manager.h"
 #include "debugger/ide-debugger-controls.h"
@@ -112,36 +114,6 @@ controls_notify_child_revealed (IdeDebuggerWorkbenchAddin *self,
 }
 
 static void
-debug_manager_breakpoint_reached (IdeDebuggerWorkbenchAddin *self,
-                                  IdeBreakpoint             *breakpoint,
-                                  IdeDebugger               *debugger)
-{
-  g_autoptr(IdeUri) uri = NULL;
-  GFile *file;
-  guint line;
-
-  g_assert (IDE_IS_DEBUGGER_WORKBENCH_ADDIN (self));
-  g_assert (IDE_IS_BREAKPOINT (breakpoint));
-  g_assert (IDE_IS_DEBUGGER (debugger));
-
-  if (NULL == (file = ide_breakpoint_get_file (breakpoint)) ||
-      NULL == (uri = ide_uri_new_from_file (file)))
-    return;
-
-  if (0 != (line = ide_breakpoint_get_line (breakpoint)))
-    {
-      g_autofree gchar *fragment = g_strdup_printf ("L%u", line);
-      ide_uri_set_fragment (uri, fragment);
-    }
-
-  ide_workbench_addin_open_async (IDE_WORKBENCH_ADDIN (self),
-                                  uri,
-                                  NULL,
-                                  IDE_WORKBENCH_OPEN_FLAGS_NONE,
-                                  NULL, NULL, NULL);
-}
-
-static void
 ide_debugger_workbench_addin_load (IdeWorkbenchAddin *addin,
                                    IdeWorkbench      *workbench)
 {
@@ -174,12 +146,6 @@ ide_debugger_workbench_addin_load (IdeWorkbenchAddin *addin,
                                    self,
                                    G_CONNECT_SWAPPED);
 
-  egg_signal_group_connect_object (self->debug_manager_signals,
-                                   "breakpoint-reached",
-                                   G_CALLBACK (debug_manager_breakpoint_reached),
-                                   self,
-                                   G_CONNECT_SWAPPED);
-
   egg_signal_group_set_target (self->debug_manager_signals, debug_manager);
 
   self->controls = g_object_new (IDE_TYPE_DEBUGGER_CONTROLS,
@@ -256,69 +222,10 @@ ide_debugger_workbench_addin_unload (IdeWorkbenchAddin *addin,
 }
 
 static void
-ide_debugger_workbench_addin_open_async (IdeWorkbenchAddin     *addin,
-                                         IdeUri                *uri,
-                                         const gchar           *content_type,
-                                         IdeWorkbenchOpenFlags  flags,
-                                         GCancellable          *cancellable,
-                                         GAsyncReadyCallback    callback,
-                                         gpointer               user_data)
-{
-  IdeDebuggerWorkbenchAddin *self = (IdeDebuggerWorkbenchAddin *)addin;
-  g_autoptr(GtkSourceFileLoader) loader = NULL;
-  g_autoptr(GtkSourceBuffer) buffer = NULL;
-  g_autoptr(GtkSourceFile) sfile = NULL;
-  g_autoptr(GTask) task = NULL;
-  g_autoptr(GFile) file = NULL;
-  GtkSourceView *view;
-
-  g_assert (IDE_IS_DEBUGGER_WORKBENCH_ADDIN (self));
-  g_assert (uri != NULL);
-  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
-
-  task = g_task_new (self, cancellable, callback, user_data);
-  g_task_set_source_tag (task, ide_debugger_workbench_addin_open_async);
-
-  file = ide_uri_to_file (uri);
-
-  buffer = gtk_source_buffer_new (NULL);
-  view = g_object_new (GTK_SOURCE_TYPE_VIEW,
-                       "buffer", buffer,
-                       "visible", TRUE,
-                       NULL);
-
-  sfile = gtk_source_file_new ();
-  gtk_source_file_set_location (sfile, file);
-
-  loader = gtk_source_file_loader_new (buffer, sfile);
-  gtk_source_file_loader_load_async (loader,
-                                     G_PRIORITY_LOW,
-                                     cancellable,
-                                     NULL,
-                                     NULL,
-                                     NULL,
-                                     NULL,
-                                     NULL);
-}
-
-static gboolean
-ide_debugger_workbench_addin_open_finish (IdeWorkbenchAddin  *addin,
-                                          GAsyncResult       *result,
-                                          GError            **error)
-{
-  g_assert (IDE_IS_DEBUGGER_WORKBENCH_ADDIN (addin));
-  g_assert (G_IS_TASK (result));
-
-  return g_task_propagate_boolean (G_TASK (result), error);
-}
-
-static void
 workbench_addin_iface_init (IdeWorkbenchAddinInterface *iface)
 {
   iface->load = ide_debugger_workbench_addin_load;
   iface->unload = ide_debugger_workbench_addin_unload;
-  iface->open_async = ide_debugger_workbench_addin_open_async;
-  iface->open_finish = ide_debugger_workbench_addin_open_finish;
 }
 
 G_DEFINE_TYPE_WITH_CODE (IdeDebuggerWorkbenchAddin, ide_debugger_workbench_addin, G_TYPE_OBJECT,
diff --git a/libide/debugger/ide-debugger.c b/libide/debugger/ide-debugger.c
index 27f0077..bbab4de 100644
--- a/libide/debugger/ide-debugger.c
+++ b/libide/debugger/ide-debugger.c
@@ -18,11 +18,15 @@
 
 #define G_LOG_DOMAIN "ide-debugger"
 
+#include <glib/gi18n.h>
+
 #include "ide-enums.h"
 #include "ide-debug.h"
 
+#include "buffers/ide-buffer.h"
+#include "files/ide-file.h"
+#include "debugger/ide-breakpoint.h"
 #include "debugger/ide-debugger.h"
-#include "diagnostics/ide-source-location.h"
 #include "runner/ide-runner.h"
 
 G_DEFINE_INTERFACE (IdeDebugger, ide_debugger, IDE_TYPE_OBJECT)
@@ -50,10 +54,151 @@ ide_debugger_real_supports_runner (IdeDebugger *self,
 }
 
 static void
+ide_debugger_real_load_source_cb (GObject      *object,
+                                  GAsyncResult *result,
+                                  gpointer      user_data)
+{
+  GtkSourceFileLoader *loader = (GtkSourceFileLoader *)object;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  GtkSourceBuffer *buffer;
+  IdeBreakpoint *breakpoint;
+  GtkTextIter iter;
+  guint line;
+  guint line_offset;
+
+  IDE_ENTRY;
+
+  g_assert (GTK_SOURCE_IS_FILE_LOADER (loader));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (G_IS_TASK (task));
+
+  if (!gtk_source_file_loader_load_finish (loader, result, &error))
+    {
+      IDE_TRACE_MSG ("Failed to load buffer: %s", error->message);
+      g_task_return_error (task, g_steal_pointer (&error));
+      IDE_EXIT;
+    }
+
+  buffer = gtk_source_file_loader_get_buffer (loader);
+  g_assert (IDE_IS_BUFFER (buffer));
+
+  breakpoint = g_task_get_task_data (task);
+  g_assert (IDE_IS_BREAKPOINT (breakpoint));
+
+  line = ide_breakpoint_get_line (breakpoint);
+  line_offset = ide_breakpoint_get_line_offset (breakpoint);
+
+  gtk_text_buffer_get_iter_at_line_offset (GTK_TEXT_BUFFER (buffer),
+                                           &iter,
+                                           line,
+                                           line_offset);
+
+  gtk_text_buffer_select_range (GTK_TEXT_BUFFER (buffer), &iter, &iter);
+
+  g_task_return_pointer (task, g_object_ref (buffer), g_object_unref);
+
+  IDE_EXIT;
+}
+
+static void
+ide_debugger_real_load_source_async (IdeDebugger         *self,
+                                     IdeBreakpoint       *breakpoint,
+                                     GCancellable        *cancellable,
+                                     GAsyncReadyCallback  callback,
+                                     gpointer             user_data)
+{
+  g_autoptr(GTask) task = NULL;
+  g_autoptr(GtkSourceFileLoader) loader = NULL;
+  g_autoptr(GtkSourceFile) source_file = NULL;
+  g_autoptr(IdeBuffer) buffer = NULL;
+  g_autoptr(IdeFile) ifile = NULL;
+  IdeContext *context;
+  GFile *file;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_DEBUGGER (self));
+  g_assert (IDE_IS_BREAKPOINT (breakpoint));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_task_data (task, g_object_ref (breakpoint), g_object_unref);
+  g_task_set_source_tag (task, ide_debugger_real_load_source_async);
+
+  file = ide_breakpoint_get_file (breakpoint);
+
+  if (file == NULL)
+    {
+      const gchar *address = ide_breakpoint_get_address (breakpoint);
+
+      if (address != NULL)
+        g_task_return_new_error (task,
+                                 G_IO_ERROR,
+                                 G_IO_ERROR_NOT_SUPPORTED,
+                                 /* translators: %s will be replaced with the address */
+                                 _("Cannot locate source for address ā€œ%sā€"),
+                                 address);
+      else
+        g_task_return_new_error (task,
+                                 G_IO_ERROR,
+                                 G_IO_ERROR_NOT_SUPPORTED,
+                                 _("Failed to locate source for breakpoint"));
+
+      IDE_EXIT;
+    }
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+
+  source_file = gtk_source_file_new ();
+  gtk_source_file_set_location (source_file, file);
+
+  ifile = g_object_new (IDE_TYPE_FILE,
+                        "context", context,
+                        "file", file,
+                        NULL);
+
+  buffer = g_object_new (IDE_TYPE_BUFFER,
+                         "file", ifile,
+                         "context", context,
+                         NULL);
+
+  loader = gtk_source_file_loader_new (GTK_SOURCE_BUFFER (buffer), source_file);
+
+  gtk_source_file_loader_load_async (loader,
+                                     G_PRIORITY_LOW,
+                                     NULL, NULL, NULL, NULL,
+                                     ide_debugger_real_load_source_cb,
+                                     g_steal_pointer (&task));
+
+  IDE_EXIT;
+}
+
+static IdeBuffer *
+ide_debugger_real_load_source_finish (IdeDebugger   *self,
+                                      GAsyncResult  *result,
+                                      GError       **error)
+{
+  IdeBuffer *ret;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_DEBUGGER (self));
+  g_assert (G_IS_TASK (result));
+
+  ret = g_task_propagate_pointer (G_TASK (result), error);
+  g_assert (!ret || IDE_IS_BUFFER (ret));
+
+  IDE_RETURN (ret);
+}
+
+static void
 ide_debugger_default_init (IdeDebuggerInterface *iface)
 {
   iface->get_name = ide_debugger_real_get_name;
   iface->supports_runner = ide_debugger_real_supports_runner;
+  iface->load_source_async = ide_debugger_real_load_source_async;
+  iface->load_source_finish = ide_debugger_real_load_source_finish;
 
   g_object_interface_install_property (iface,
                                        g_param_spec_boolean ("can-step-in",
@@ -96,11 +241,11 @@ ide_debugger_default_init (IdeDebuggerInterface *iface)
    * IdeDebugger::stopped:
    * @self: An #IdeDebugger
    * @reason: An #IdeDebuggerStopReason for why the stop occurred
-   * @location: An #IdeSourceLocation of where the debugger has stopped
+   * @breakpoint: An #IdeBreakpoint
    *
    * The "stopped" signal should be emitted when the debugger has stopped at a
-   * new location. @reason indicates the reson for the stop, and @location is
-   * the location where the stop has occurred.
+   * new location. @reason indicates the reson for the stop, and @breakpoint
+   * describes the current location within the appliation.
    */
   signals [STOPPED] =
     g_signal_new ("stopped",
@@ -108,8 +253,7 @@ ide_debugger_default_init (IdeDebuggerInterface *iface)
                   G_SIGNAL_RUN_LAST,
                   G_STRUCT_OFFSET (IdeDebuggerInterface, stopped),
                   NULL, NULL, NULL,
-                  G_TYPE_NONE,
-                  2, IDE_TYPE_DEBUGGER_STOP_REASON, IDE_TYPE_SOURCE_LOCATION);
+                  G_TYPE_NONE, 2, IDE_TYPE_DEBUGGER_STOP_REASON, IDE_TYPE_BREAKPOINT);
 }
 
 /**
@@ -169,14 +313,47 @@ ide_debugger_get_name (IdeDebugger *self)
   return ret;
 }
 
+/**
+ * ide_debugger_emit_stopped:
+ * @self: A #IdeDebugger
+ * @reason: the reason for the stop
+ * @breakpoint: (nullable): an optional breakpoint that is reached
+ *
+ * This signal should be emitted when the debugger has stopped executing.
+ *
+ * If the stop is not related to the application exiting for any reason, then
+ * you should provide a @breakpoint describing the stop location. That
+ * breakpoint may be transient (see #IdeDebugger:transient) meaning that it is
+ * only created for this stop event.
+ */
 void
 ide_debugger_emit_stopped (IdeDebugger           *self,
                            IdeDebuggerStopReason  reason,
-                           IdeSourceLocation     *location)
+                           IdeBreakpoint         *breakpoint)
 {
   g_return_if_fail (IDE_IS_DEBUGGER (self));
+  g_return_if_fail (!breakpoint || IDE_IS_BREAKPOINT (breakpoint));
+
+#ifdef IDE_ENABLE_TRACE
+  if (breakpoint != NULL)
+    {
+      g_autofree gchar *uri = NULL;
+      const gchar *address;
+      GFile *file;
+
+      address = ide_breakpoint_get_address (breakpoint);
+      file = ide_breakpoint_get_file (breakpoint);
+
+      if (file != NULL)
+        uri = g_file_get_uri (file);
+
+      IDE_TRACE_MSG ("Stopped at breakpoint \"%s\" with address \"%s\"",
+                     uri ? uri : "<none>",
+                     address ? address : "<none>");
+    }
+#endif
 
-  g_signal_emit (self, signals [STOPPED], 0, reason, location);
+  g_signal_emit (self, signals [STOPPED], 0, reason, breakpoint);
 }
 
 void
@@ -209,3 +386,50 @@ ide_debugger_emit_log (IdeDebugger *self,
   if (message != NULL)
     g_signal_emit (self, signals [LOG], 0, message);
 }
+
+void
+ide_debugger_load_source_async (IdeDebugger         *self,
+                                IdeBreakpoint       *breakpoint,
+                                GCancellable        *cancellable,
+                                GAsyncReadyCallback  callback,
+                                gpointer             user_data)
+{
+  IDE_ENTRY;
+
+  g_return_if_fail (IDE_IS_DEBUGGER (self));
+  g_return_if_fail (IDE_IS_BREAKPOINT (breakpoint));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  IDE_DEBUGGER_GET_IFACE (self)->load_source_async (self, breakpoint, cancellable, callback, user_data);
+
+  IDE_EXIT;
+}
+
+/**
+ * ide_debugger_load_source_finish:
+ * @self: a #IdeDebugger
+ * @result: a #GAsyncResult
+ * @error: a location for a #GError, or %NULL
+ *
+ * Completes an asynchronous request to ide_debugger_load_source_async().
+ *
+ * Returns: (nullable) (transfer full): An #IdeBuffer or %NULL
+ */
+IdeBuffer *
+ide_debugger_load_source_finish (IdeDebugger   *self,
+                                 GAsyncResult  *result,
+                                 GError       **error)
+{
+  IdeBuffer *ret;
+
+  IDE_ENTRY;
+
+  g_return_val_if_fail (IDE_IS_DEBUGGER (self), NULL);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
+
+  ret = IDE_DEBUGGER_GET_IFACE (self)->load_source_finish (self, result, error);
+
+  g_return_val_if_fail (!ret || IDE_IS_BUFFER (ret), NULL);
+
+  IDE_RETURN (ret);
+}
diff --git a/libide/debugger/ide-debugger.h b/libide/debugger/ide-debugger.h
index 843920a..7b5e557 100644
--- a/libide/debugger/ide-debugger.h
+++ b/libide/debugger/ide-debugger.h
@@ -19,6 +19,8 @@
 #ifndef IDE_DEBUGGER_H
 #define IDE_DEBUGGER_H
 
+#include <gtksourceview/gtksource.h>
+
 #include "ide-object.h"
 
 G_BEGIN_DECLS
@@ -48,34 +50,50 @@ struct _IdeDebuggerInterface
 {
   GTypeInterface parent_iface;
 
-  gchar    *(*get_name)        (IdeDebugger            *self);
-  gboolean  (*supports_runner) (IdeDebugger            *self,
-                                IdeRunner              *runner,
-                                gint                   *priority);
-  void      (*stopped)         (IdeDebugger            *self,
-                                IdeDebuggerStopReason   reason,
-                                IdeSourceLocation      *location);
-  void      (*prepare)         (IdeDebugger            *debugger,
-                                IdeRunner              *runner);
-  void      (*run)             (IdeDebugger            *self,
-                                IdeDebuggerRunType      run_type);
-  void      (*log)             (IdeDebugger            *self,
-                                const gchar            *message);
+  gchar           *(*get_name)           (IdeDebugger            *self);
+  gboolean         (*supports_runner)    (IdeDebugger            *self,
+                                          IdeRunner              *runner,
+                                          gint                   *priority);
+  void             (*stopped)            (IdeDebugger            *self,
+                                          IdeDebuggerStopReason   reason,
+                                          IdeBreakpoint          *breakpoint);
+  void             (*prepare)            (IdeDebugger            *debugger,
+                                          IdeRunner              *runner);
+  void             (*run)                (IdeDebugger            *self,
+                                          IdeDebuggerRunType      run_type);
+  void             (*log)                (IdeDebugger            *self,
+                                          const gchar            *message);
+  void             (*load_source_async)  (IdeDebugger            *self,
+                                          IdeBreakpoint          *breakpoint,
+                                          GCancellable           *cancellable,
+                                          GAsyncReadyCallback     callback,
+                                          gpointer                user_data);
+  IdeBuffer       *(*load_source_finish) (IdeDebugger            *self,
+                                          GAsyncResult           *result,
+                                          GError                **error);
 };
 
-gchar    *ide_debugger_get_name        (IdeDebugger           *self);
-gboolean  ide_debugger_supports_runner (IdeDebugger           *self,
-                                        IdeRunner             *runner,
-                                        gint                  *priority);
-void      ide_debugger_prepare         (IdeDebugger           *self,
-                                        IdeRunner             *runner);
-void      ide_debugger_run             (IdeDebugger           *self,
-                                        IdeDebuggerRunType     run_type);
-void      ide_debugger_emit_log        (IdeDebugger           *self,
-                                        const gchar           *message);
-void      ide_debugger_emit_stopped    (IdeDebugger           *self,
-                                        IdeDebuggerStopReason  reason,
-                                        IdeSourceLocation     *location);
+gchar           *ide_debugger_get_name           (IdeDebugger            *self);
+gboolean         ide_debugger_supports_runner    (IdeDebugger            *self,
+                                                  IdeRunner              *runner,
+                                                  gint                   *priority);
+void             ide_debugger_prepare            (IdeDebugger            *self,
+                                                  IdeRunner              *runner);
+void             ide_debugger_run                (IdeDebugger            *self,
+                                                  IdeDebuggerRunType      run_type);
+void             ide_debugger_emit_log           (IdeDebugger            *self,
+                                                  const gchar            *message);
+void             ide_debugger_emit_stopped       (IdeDebugger            *self,
+                                                  IdeDebuggerStopReason   reason,
+                                                  IdeBreakpoint          *breakpoint);
+void             ide_debugger_load_source_async  (IdeDebugger            *self,
+                                                  IdeBreakpoint          *breakpoint,
+                                                  GCancellable           *cancellable,
+                                                  GAsyncReadyCallback     callback,
+                                                  gpointer                user_data);
+IdeBuffer       *ide_debugger_load_source_finish (IdeDebugger            *self,
+                                                  GAsyncResult           *result,
+                                                  GError                **error);
 
 G_END_DECLS
 
diff --git a/libide/ide-types.h b/libide/ide-types.h
index 206d78d..abe79a1 100644
--- a/libide/ide-types.h
+++ b/libide/ide-types.h
@@ -51,6 +51,7 @@ typedef struct _IdeConfigurationManager        IdeConfigurationManager;
 typedef struct _IdeContext                     IdeContext;
 
 typedef struct _IdeDebugger                    IdeDebugger;
+typedef struct _IdeDebuggerBreakpoints         IdeDebuggerBreakpoints;
 typedef struct _IdeBreakpoint                  IdeBreakpoint;
 typedef struct _IdeDebugManager                IdeDebugManager;
 
diff --git a/libide/ide.h b/libide/ide.h
index 7067844..27c8031 100644
--- a/libide/ide.h
+++ b/libide/ide.h
@@ -53,6 +53,7 @@ G_BEGIN_DECLS
 #include "buildsystem/ide-configuration-provider.h"
 #include "buildsystem/ide-environment-variable.h"
 #include "buildsystem/ide-environment.h"
+#include "debugger/ide-breakpoint.h"
 #include "debugger/ide-debug-manager.h"
 #include "debugger/ide-debugger.h"
 #include "devices/ide-device-manager.h"
diff --git a/libide/resources/libide.gresource.xml b/libide/resources/libide.gresource.xml
index 8d4d916..10668ba 100644
--- a/libide/resources/libide.gresource.xml
+++ b/libide/resources/libide.gresource.xml
@@ -53,6 +53,7 @@
   <gresource prefix="/org/gnome/builder/ui">
     <file compressed="true" alias="ide-debugger-controls.ui">../debugger/ide-debugger-controls.ui</file>
     <file compressed="true" 
alias="ide-debugger-perspective.ui">../debugger/ide-debugger-perspective.ui</file>
+    <file compressed="true" alias="ide-debugger-view.ui">../debugger/ide-debugger-view.ui</file>
     <file compressed="true" alias="ide-editor-frame.ui">../editor/ide-editor-frame.ui</file>
     <file compressed="true" 
alias="ide-editor-layout-stack-controls.ui">../editor/ide-editor-layout-stack-controls.ui</file>
     <file compressed="true" alias="ide-editor-perspective.ui">../editor/ide-editor-perspective.ui</file>
diff --git a/plugins/gdb/gbp-gdb-debugger.c b/plugins/gdb/gbp-gdb-debugger.c
index f183674..d4e592c 100644
--- a/plugins/gdb/gbp-gdb-debugger.c
+++ b/plugins/gdb/gbp-gdb-debugger.c
@@ -327,8 +327,8 @@ gbp_gdb_debugger_on_client_stopped (GbpGdbDebugger  *self,
                                     Mi2EventMessage *message,
                                     Mi2Client       *client)
 {
+  g_autoptr(IdeBreakpoint) breakpoint = NULL;
   IdeDebuggerStopReason translated;
-  g_autoptr(IdeSourceLocation) location = NULL;
   GVariant *fra;
 
   IDE_ENTRY;
@@ -359,32 +359,37 @@ gbp_gdb_debugger_on_client_stopped (GbpGdbDebugger  *self,
       break;
     }
 
-  if (NULL != (fra = mi2_message_get_param (MI2_MESSAGE (message), "frame")) &&
-      g_variant_is_of_type (fra, G_VARIANT_TYPE ("a{sv}")))
+  fra = mi2_message_get_param (MI2_MESSAGE (message), "frame");
+
+  if (fra != NULL && g_variant_is_of_type (fra, G_VARIANT_TYPE ("a{sv}")))
     {
-      GVariantDict dict;
+      const gchar *address = NULL;
       const gchar *filename = NULL;
-      const gchar *line = NULL;
+      const gchar *linestr = NULL;
+      GVariantDict dict;
 
       g_variant_dict_init (&dict, fra);
       g_variant_dict_lookup (&dict, "fullname", "&s", &filename);
-      g_variant_dict_lookup (&dict, "line", "&s", &line);
+      g_variant_dict_lookup (&dict, "line", "&s", &linestr);
+      g_variant_dict_lookup (&dict, "addr", "&s", &address);
 
-      if (filename != NULL && line != NULL)
+      if (filename != NULL && linestr != NULL)
         {
-          IdeContext *context = ide_object_get_context (IDE_OBJECT (self));
-          g_autoptr(GFile) gfile = g_file_new_for_path (filename);
-          g_autoptr(IdeFile) ifile = ide_file_new (context, gfile);
-
-          location = ide_source_location_new (ifile,
-                                              g_ascii_strtoll (line, NULL, 10),
-                                              0, 0);
+          g_autoptr(GFile) file = g_file_new_for_path (filename);
+          guint line = g_ascii_strtoll (linestr, NULL, 10);
+
+          breakpoint = g_object_new (IDE_TYPE_BREAKPOINT,
+                                     "address", address,
+                                     "file", file,
+                                     "line", line,
+                                     "line-offset", 0,
+                                     NULL);
         }
     }
 
   gbp_gdb_debugger_notify_properties (self);
 
-  ide_debugger_emit_stopped (IDE_DEBUGGER (self), translated, NULL);
+  ide_debugger_emit_stopped (IDE_DEBUGGER (self), translated, breakpoint);
 
   IDE_EXIT;
 }
diff --git a/po/POTFILES.in b/po/POTFILES.in
index b911211..1910c16 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -54,6 +54,7 @@ libide/buildui/ide-build-panel.ui
 libide/buildui/ide-build-perspective.c
 libide/buildui/ide-build-tool.c
 libide/buildui/ide-environment-editor.c
+libide/debugger/ide-debugger.c
 libide/devices/ide-device-manager.c
 libide/directory/ide-directory-vcs.c
 libide/editorconfig/ide-editorconfig-file-settings.c


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]