[gnome-builder/wip/chergert/bug1: 5/35] perspective port wip



commit e679496d235dfca3dcab8238bac4033e71699c03
Author: Christian Hergert <chergert redhat com>
Date:   Fri Aug 25 01:33:49 2017 -0700

    perspective port wip

 data/gtk/menus.ui                                  |   18 +
 data/icons/hicolor/icons.gresource.xml             |    1 +
 .../scalable/actions/builder-debugger-symbolic.svg |   27 ++
 libide/debugger/debugger.plugin                    |   10 +
 libide/debugger/ide-debug-manager.c                |  100 +++++-
 libide/debugger/ide-debug-manager.h                |   11 +-
 libide/debugger/ide-debugger-breakpoints-view.ui   |  121 +++++++
 libide/debugger/ide-debugger-breakpoints.c         |  189 ++++++++++
 libide/debugger/ide-debugger-breakpoints.h         |   37 ++
 libide/debugger/ide-debugger-controls.c            |   40 ++
 libide/debugger/ide-debugger-controls.h            |   40 ++
 libide/debugger/ide-debugger-controls.ui           |   92 +++++
 libide/debugger/ide-debugger-gutter-renderer.c     |  288 +++++++++++++++
 libide/debugger/ide-debugger-gutter-renderer.h     |   37 ++
 libide/debugger/ide-debugger-libraries-view.ui     |   44 +++
 libide/debugger/ide-debugger-locals-view.ui        |   61 ++++
 libide/debugger/ide-debugger-perspective.c         |  376 ++++++++++++++++++++
 libide/debugger/ide-debugger-perspective.h         |   35 ++
 libide/debugger/ide-debugger-perspective.ui        |  108 ++++++
 libide/debugger/ide-debugger-plugin.c              |   40 ++
 libide/debugger/ide-debugger-private.h             |  142 ++++----
 libide/debugger/ide-debugger-registers-view.ui     |   52 +++
 libide/debugger/ide-debugger-threads-view.ui       |  140 ++++++++
 libide/debugger/ide-debugger-types.c               |    1 +
 libide/debugger/ide-debugger-types.h               |    2 +
 libide/debugger/ide-debugger-view.c                |  155 ++++++++
 libide/debugger/ide-debugger-view.h                |   36 ++
 libide/debugger/ide-debugger-view.ui               |   20 +
 libide/debugger/ide-debugger-workbench-addin.c     |  260 ++++++++++++++
 libide/debugger/ide-debugger-workbench-addin.h     |   29 ++
 libide/ide-context.h                               |    1 +
 libide/ide-types.h                                 |    8 +
 libide/libide.gresource.xml                        |   12 +
 libide/meson.build                                 |   13 +
 34 files changed, 2467 insertions(+), 79 deletions(-)
---
diff --git a/data/gtk/menus.ui b/data/gtk/menus.ui
index e849629..355c5f9 100644
--- a/data/gtk/menus.ui
+++ b/data/gtk/menus.ui
@@ -51,6 +51,16 @@
         <attribute name="target">editor</attribute>
         <attribute name="verb-icon-name">builder-editor-symbolic</attribute>
       </item>
+      <item>
+        <attribute name="accel">&lt;alt&gt;2</attribute>
+        <attribute name="action">win.perspective</attribute>
+        <attribute name="after">perspective-menu-editor</attribute>
+        <attribute name="id">perspective-menu-debugger</attribute>
+        <attribute name="label" translatable="yes">Debugger</attribute>
+        <attribute name="role">normal</attribute>
+        <attribute name="target">debugger</attribute>
+        <attribute name="verb-icon-name">builder-debugger-symbolic</attribute>
+      </item>
     </section>
     <section id="perspectives-menu-prefs-section">
       <item>
@@ -332,6 +342,14 @@
         <attribute name="verb-icon-name">media-playback-start-symbolic</attribute>
         <attribute name="accel">&lt;Control&gt;F5</attribute>
       </item>
+      <item>
+        <attribute name="id">debugger-run-handler</attribute>
+        <attribute name="action">run-manager.run-with-handler</attribute>
+        <attribute name="target">debugger</attribute>
+        <attribute name="label" translatable="yes">Run with Debugger</attribute>
+        <attribute name="verb-icon-name">builder-debugger-symbolic</attribute>
+        <attribute name="accel">F5</attribute>
+      </item>
     </section>
   </menu>
 </interface>
diff --git a/data/icons/hicolor/icons.gresource.xml b/data/icons/hicolor/icons.gresource.xml
index d078183..f89cd16 100644
--- a/data/icons/hicolor/icons.gresource.xml
+++ b/data/icons/hicolor/icons.gresource.xml
@@ -12,6 +12,7 @@
     <file compressed="true">scalable/actions/builder-view-bottom-pane-symbolic.svg</file>
     <file compressed="true">scalable/actions/builder-view-left-pane-symbolic.svg</file>
     <file compressed="true">scalable/actions/builder-view-right-pane-symbolic.svg</file>
+    <file compressed="true">scalable/actions/builder-debugger-symbolic.svg</file>
     <file compressed="true">scalable/actions/debug-continue-symbolic.svg</file>
     <file compressed="true">scalable/actions/debug-execute-from-cursor-symbolic.svg</file>
     <file compressed="true">scalable/actions/debug-execute-to-cursor-symbolic.svg</file>
diff --git a/data/icons/hicolor/scalable/actions/builder-debugger-symbolic.svg 
b/data/icons/hicolor/scalable/actions/builder-debugger-symbolic.svg
new file mode 100644
index 0000000..8aac791
--- /dev/null
+++ b/data/icons/hicolor/scalable/actions/builder-debugger-symbolic.svg
@@ -0,0 +1,27 @@
+<?xml version='1.0' encoding='UTF-8' standalone='no'?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg xmlns:cc='http://creativecommons.org/ns#' xmlns:dc='http://purl.org/dc/elements/1.1/' 
sodipodi:docname='nemiver-symbolic.svg' height='16' id='svg7384' 
xmlns:inkscape='http://www.inkscape.org/namespaces/inkscape' 
xmlns:osb='http://www.openswatchbook.org/uri/2009/osb' 
xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' 
xmlns:sodipodi='http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd' xmlns:svg='http://www.w3.org/2000/svg' 
version='1.1' inkscape:version='0.91 r13725' width='16' xmlns='http://www.w3.org/2000/svg'>
+  <metadata id='metadata90'>
+    <rdf:RDF>
+      <cc:Work rdf:about=''>
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type rdf:resource='http://purl.org/dc/dcmitype/StillImage'/>
+        <dc:title>Gnome Symbolic Icon Theme</dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview inkscape:bbox-paths='true' bordercolor='#666666' borderopacity='1' 
inkscape:current-layer='layer9' inkscape:cx='5.1146516' inkscape:cy='-47.54864' gridtolerance='10' 
inkscape:guide-bbox='true' guidetolerance='10' id='namedview88' inkscape:object-nodes='false' 
inkscape:object-paths='false' objecttolerance='10' pagecolor='#555753' inkscape:pageopacity='1' 
inkscape:pageshadow='2' showborder='false' showgrid='false' showguides='true' inkscape:snap-bbox='true' 
inkscape:snap-bbox-midpoints='false' inkscape:snap-global='true' inkscape:snap-grids='true' 
inkscape:snap-nodes='true' inkscape:snap-others='false' inkscape:snap-to-guides='true' 
inkscape:window-height='1376' inkscape:window-maximized='1' inkscape:window-width='2560' 
inkscape:window-x='0' inkscape:window-y='27' inkscape:zoom='4'>
+    <inkscape:grid empspacing='2' enabled='true' id='grid4866' originx='-162' originy='-382' 
snapvisiblegridlinesonly='true' spacingx='1px' spacingy='1px' type='xygrid' visible='true'/>
+  </sodipodi:namedview>
+  <title id='title9167'>Gnome Symbolic Icon Theme</title>
+  <defs id='defs7386'>
+    <linearGradient id='linearGradient7212' osb:paint='solid'>
+      <stop id='stop7214' offset='0' style='stop-color:#000000;stop-opacity:1;'/>
+    </linearGradient>
+  </defs>
+  <g inkscape:groupmode='layer' id='layer9' inkscape:label='apps' style='display:inline' 
transform='translate(-403.0002,165)'>
+
+    <path inkscape:connector-curvature='0' d='m 409.46895,-165 c -0.25969,0 -0.46875,0.20906 
-0.46875,0.46875 l 0,1.875 c -1.03854,0.49004 -1.79689,1.48201 -1.96875,2.65625 l 6.9375,0 c 
-0.17186,-1.17424 -0.93021,-2.16621 -1.96875,-2.65625 l 0,-1.875 c 0,-0.25969 -0.20906,-0.46875 
-0.46875,-0.46875 z m 0.0312,1 c 0.27614,0 0.5,0.22386 0.5,0.5 0,0.27614 -0.22386,0.5 -0.5,0.5 -0.27614,0 
-0.5,-0.22386 -0.5,-0.5 0,-0.27614 0.22386,-0.5 0.5,-0.5 z m -2.5,5 0,1 4.5,0 c 0.26179,3e-5 0.49997,0.23821 
0.5,0.5 l 0,6 c -3e-5,0.26179 -0.23821,0.49997 -0.5,0.5 l -4.5,0 0,0.96875 c 0,0.57402 0.45723,1.03125 
1.03125,1.03125 l 4.9375,0 c 0.57402,0 1.03125,-0.45723 1.03125,-1.03125 l 0,-8.96875 z m 0,2 0,5 4,0 0,-5 z' 
id='rect7914' sodipodi:nodetypes='ssccccsssssssscccccccssssccccccc' 
style='fill:#bebebe;fill-opacity:1;stroke:none'/>
+  </g>
+</svg>
diff --git a/libide/debugger/debugger.plugin b/libide/debugger/debugger.plugin
new file mode 100644
index 0000000..8ef8a11
--- /dev/null
+++ b/libide/debugger/debugger.plugin
@@ -0,0 +1,10 @@
+[Plugin]
+Module=debugger
+Name=Debugger
+Description=Debugger integration for Builder
+Authors=Christian Hergert <chergert redhat com>
+Copyright=Copyright © 2017 Christian Hergert
+Depends=editor
+Builtin=true
+Hidden=true
+Embedded=ide_debugger_register_types
diff --git a/libide/debugger/ide-debug-manager.c b/libide/debugger/ide-debug-manager.c
index ee7f6a9..82cf14b 100644
--- a/libide/debugger/ide-debug-manager.c
+++ b/libide/debugger/ide-debug-manager.c
@@ -25,6 +25,7 @@
 
 #include "debugger/ide-debug-manager.h"
 #include "debugger/ide-debugger.h"
+#include "debugger/ide-debugger-private.h"
 #include "plugins/ide-extension-util.h"
 #include "runner/ide-runner.h"
 
@@ -32,6 +33,7 @@ struct _IdeDebugManager
 {
   IdeObject           parent_instance;
 
+  GHashTable         *breakpoints;
   IdeDebugger        *debugger;
   DzlBindingGroup    *debugger_bindings;
   DzlSignalGroup     *debugger_signals;
@@ -125,6 +127,63 @@ ide_debug_manager_debugger_stopped (IdeDebugManager       *self,
 }
 
 static void
+ide_debug_manager_breakpoint_added (IdeDebugManager       *self,
+                                    IdeDebuggerBreakpoint *breakpoint,
+                                    IdeDebugger           *debugger)
+{
+  IdeDebuggerBreakpoints *breakpoints;
+  IdeDebuggerBreakMode mode;
+  g_autoptr(GFile) file = NULL;
+  const gchar *path;
+  guint line;
+
+  g_assert (IDE_IS_DEBUG_MANAGER (self));
+  g_assert (IDE_IS_DEBUGGER_BREAKPOINT (breakpoint));
+  g_assert (IDE_IS_DEBUGGER (debugger));
+
+  path = ide_debugger_breakpoint_get_file (breakpoint);
+  file = g_file_new_for_path (path);
+
+  breakpoints = g_hash_table_lookup (self->breakpoints, file);
+
+  if (breakpoints == NULL)
+    {
+      breakpoints = g_object_new (IDE_TYPE_DEBUGGER_BREAKPOINTS,
+                                  "file", file,
+                                  NULL);
+      g_hash_table_insert (self->breakpoints, g_steal_pointer (&file), breakpoints);
+    }
+
+  mode = ide_debugger_breakpoint_get_mode (breakpoint);
+  line = ide_debugger_breakpoint_get_line (breakpoint);
+
+  ide_debugger_breakpoints_set_line (breakpoints, line, mode);
+}
+
+static void
+ide_debug_manager_breakpoint_removed (IdeDebugManager       *self,
+                                      IdeDebuggerBreakpoint *breakpoint,
+                                      IdeDebugger           *debugger)
+{
+  IdeDebuggerBreakpoints *breakpoints;
+  g_autoptr(GFile) file = NULL;
+  const gchar *path;
+  guint line;
+
+  g_assert (IDE_IS_DEBUG_MANAGER (self));
+  g_assert (IDE_IS_DEBUGGER_BREAKPOINT (breakpoint));
+  g_assert (IDE_IS_DEBUGGER (debugger));
+
+  line = ide_debugger_breakpoint_get_line (breakpoint);
+  path = ide_debugger_breakpoint_get_file (breakpoint);
+  file = g_file_new_for_path (path);
+
+  breakpoints = g_hash_table_lookup (self->breakpoints, file);
+  if (breakpoints != NULL)
+    ide_debugger_breakpoints_set_line (breakpoints, line, IDE_DEBUGGER_BREAK_NONE);
+}
+
+static void
 ide_debug_manager_finalize (GObject *object)
 {
   IdeDebugManager *self = (IdeDebugManager *)object;
@@ -133,6 +192,7 @@ ide_debug_manager_finalize (GObject *object)
   g_clear_object (&self->debugger_bindings);
   g_clear_object (&self->debugger_signals);
   g_clear_object (&self->runner);
+  g_clear_pointer (&self->breakpoints, g_hash_table_unref);
 
   G_OBJECT_CLASS (ide_debug_manager_parent_class)->finalize (object);
 }
@@ -217,13 +277,27 @@ ide_debug_manager_class_init (IdeDebugManagerClass *klass)
 static void
 ide_debug_manager_init (IdeDebugManager *self)
 {
+  self->breakpoints = g_hash_table_new_full ((GHashFunc)g_file_hash,
+                                             (GEqualFunc)g_file_equal,
+                                             g_object_unref,
+                                             g_object_unref);
+
   self->debugger_signals = dzl_signal_group_new (IDE_TYPE_DEBUGGER);
 
-  dzl_signal_group_connect_object (self->debugger_signals,
-                                   "stopped",
-                                   G_CALLBACK (ide_debug_manager_debugger_stopped),
-                                   self,
-                                   G_CONNECT_SWAPPED);
+  dzl_signal_group_connect_swapped (self->debugger_signals,
+                                    "stopped",
+                                    G_CALLBACK (ide_debug_manager_debugger_stopped),
+                                    self);
+
+  dzl_signal_group_connect_swapped (self->debugger_signals,
+                                    "breakpoint-added",
+                                    G_CALLBACK (ide_debug_manager_breakpoint_added),
+                                    self);
+
+  dzl_signal_group_connect_swapped (self->debugger_signals,
+                                    "breakpoint-removed",
+                                    G_CALLBACK (ide_debug_manager_breakpoint_removed),
+                                    self);
 }
 
 static void
@@ -387,3 +461,19 @@ ide_debug_manager_get_active (IdeDebugManager *self)
 
   return self->active;
 }
+
+/**
+ * ide_debug_manager_get_debugger:
+ * @self: a #IdeDebugManager
+ *
+ * Gets the debugger instance, if it is loaded.
+ *
+ * Returns: (transfer none) (nullable): An #IdeDebugger or %NULL
+ */
+IdeDebugger *
+ide_debug_manager_get_debugger (IdeDebugManager *self)
+{
+  g_return_val_if_fail (IDE_IS_DEBUG_MANAGER (self), NULL);
+
+  return self->debugger;
+}
diff --git a/libide/debugger/ide-debug-manager.h b/libide/debugger/ide-debug-manager.h
index e0bdbf7..fa2a18b 100644
--- a/libide/debugger/ide-debug-manager.h
+++ b/libide/debugger/ide-debug-manager.h
@@ -26,10 +26,11 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (IdeDebugManager, ide_debug_manager, IDE, DEBUG_MANAGER, IdeObject)
 
-gboolean ide_debug_manager_get_active (IdeDebugManager  *self);
-gboolean ide_debug_manager_start      (IdeDebugManager  *self,
-                                       IdeRunner        *runner,
-                                       GError          **error);
-void     ide_debug_manager_stop       (IdeDebugManager  *self);
+IdeDebugger *ide_debug_manager_get_debugger (IdeDebugManager  *self);
+gboolean     ide_debug_manager_get_active   (IdeDebugManager  *self);
+gboolean     ide_debug_manager_start        (IdeDebugManager  *self,
+                                             IdeRunner        *runner,
+                                             GError          **error);
+void         ide_debug_manager_stop         (IdeDebugManager  *self);
 
 G_END_DECLS
diff --git a/libide/debugger/ide-debugger-breakpoints-view.ui 
b/libide/debugger/ide-debugger-breakpoints-view.ui
new file mode 100644
index 0000000..0a184de
--- /dev/null
+++ b/libide/debugger/ide-debugger-breakpoints-view.ui
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="IdeDebuggerBreakpointsView" parent="GtkBin">
+    <child>
+      <object class="GtkScrolledWindow">
+        <property name="visible">true</property>
+        <child>
+          <object class="GtkTreeView" id="tree_view">
+            <property name="model">list_store</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkTreeViewColumn" id="enabled_column">
+                <child>
+                  <object class="GtkCellRendererToggle" id="enabled_cell">
+                    <property name="xalign">0.5</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkTreeViewColumn" id="id_column">
+                <property name="title" translatable="yes">ID</property>
+                <child>
+                  <object class="GtkCellRendererText" id="id_cell">
+                    <property name="xalign">0.0</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkTreeViewColumn" id="file_column">
+                <property name="expand">true</property>
+                <property name="resizable">true</property>
+                <property name="title" translatable="yes">File</property>
+                <child>
+                  <object class="GtkCellRendererText" id="file_cell">
+                    <property name="ellipsize">start</property>
+                    <property name="xalign">0.0</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkTreeViewColumn" id="line_column">
+                <property name="resizable">true</property>
+                <property name="title" translatable="yes">Line</property>
+                <child>
+                  <object class="GtkCellRendererText" id="line_cell">
+                    <property name="xalign">0.0</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkTreeViewColumn" id="function_column">
+                <property name="expand">true</property>
+                <property name="resizable">true</property>
+                <property name="title" translatable="yes">Function</property>
+                <child>
+                  <object class="GtkCellRendererText" id="function_cell">
+                    <property name="ellipsize">middle</property>
+                    <property name="xalign">0.0</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkTreeViewColumn" id="address_column">
+                <property name="resizable">true</property>
+                <property name="title" translatable="yes">Address</property>
+                <child>
+                  <object class="GtkCellRendererText" id="address_cell">
+                    <property name="xalign">0.0</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkTreeViewColumn" id="type_column">
+                <property name="resizable">true</property>
+                <property name="title" translatable="yes">Type</property>
+                <child>
+                  <object class="GtkCellRendererText" id="type_cell">
+                    <property name="xalign">0.0</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkTreeViewColumn" id="hits_column">
+                <property name="title" translatable="yes">Hits</property>
+                <child>
+                  <object class="GtkCellRendererText" id="hits_cell">
+                    <property name="xalign">0.0</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkTreeViewColumn" id="spec_column">
+                <property name="resizable">true</property>
+                <property name="title" translatable="yes">Expression</property>
+                <child>
+                  <object class="GtkCellRendererText" id="spec_cell">
+                    <property name="ellipsize">end</property>
+                    <property name="xalign">0.0</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+  <object class="GtkListStore" id="list_store">
+    <columns>
+      <column type="IdeDebuggerBreakpoint"/>
+    </columns>
+  </object>
+</interface>
diff --git a/libide/debugger/ide-debugger-breakpoints.c b/libide/debugger/ide-debugger-breakpoints.c
new file mode 100644
index 0000000..d65567c
--- /dev/null
+++ b/libide/debugger/ide-debugger-breakpoints.c
@@ -0,0 +1,189 @@
+/* ide-debugger-breakpoints.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-breakpoints"
+
+#include <stdlib.h>
+
+#include "ide-debug.h"
+
+#include "debugger/ide-debugger-breakpoints.h"
+
+typedef struct
+{
+  guint line;
+  IdeDebuggerBreakMode mode;
+} LineInfo;
+
+struct _IdeDebuggerBreakpoints
+{
+  GObject parent_instance;
+  GArray *lines;
+  GFile *file;
+};
+
+enum {
+  PROP_0,
+  PROP_FILE,
+  N_PROPS
+};
+
+G_DEFINE_TYPE (IdeDebuggerBreakpoints, ide_debugger_breakpoints, G_TYPE_OBJECT)
+
+static GParamSpec *properties [N_PROPS];
+
+static gint
+line_info_compare (gconstpointer a,
+                   gconstpointer b)
+{
+  const LineInfo *lia = a;
+  const LineInfo *lib = b;
+
+  return (gint)lia->line - (gint)lib->line;
+}
+
+static void
+ide_debugger_breakpoints_finalize (GObject *object)
+{
+  IdeDebuggerBreakpoints *self = (IdeDebuggerBreakpoints *)object;
+
+  g_clear_object (&self->file);
+  g_clear_pointer (&self->lines, g_array_unref);
+
+  G_OBJECT_CLASS (ide_debugger_breakpoints_parent_class)->finalize (object);
+}
+
+static void
+ide_debugger_breakpoints_get_property (GObject    *object,
+                                       guint       prop_id,
+                                       GValue     *value,
+                                       GParamSpec *pspec)
+{
+  IdeDebuggerBreakpoints *self = IDE_DEBUGGER_BREAKPOINTS (object);
+
+  switch (prop_id)
+    {
+    case PROP_FILE:
+      g_value_set_object (value, self->file);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_debugger_breakpoints_set_property (GObject      *object,
+                                       guint         prop_id,
+                                       const GValue *value,
+                                       GParamSpec   *pspec)
+{
+  IdeDebuggerBreakpoints *self = IDE_DEBUGGER_BREAKPOINTS (object);
+
+  switch (prop_id)
+    {
+    case PROP_FILE:
+      self->file = g_value_dup_object (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_debugger_breakpoints_class_init (IdeDebuggerBreakpointsClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_debugger_breakpoints_finalize;
+  object_class->get_property = ide_debugger_breakpoints_get_property;
+  object_class->set_property = ide_debugger_breakpoints_set_property;
+
+  properties [PROP_FILE] =
+    g_param_spec_object ("file",
+                         "File",
+                         "The file for the breakpoints",
+                         G_TYPE_FILE,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+ide_debugger_breakpoints_init (IdeDebuggerBreakpoints *self)
+{
+}
+
+IdeDebuggerBreakMode
+ide_debugger_breakpoints_get_line (IdeDebuggerBreakpoints *self,
+                                   guint                   line)
+{
+  g_return_val_if_fail (IDE_IS_DEBUGGER_BREAKPOINTS (self), 0);
+
+  if (self->lines != NULL)
+    {
+      LineInfo info = { line, 0 };
+      LineInfo *ret;
+
+      ret = bsearch (&info, (gpointer)self->lines->data,
+                     self->lines->len, sizeof (LineInfo),
+                     line_info_compare);
+
+      if (ret)
+        return ret->mode;
+    }
+
+  return 0;
+}
+
+void
+ide_debugger_breakpoints_set_line (IdeDebuggerBreakpoints *self,
+                                   guint                   line,
+                                   IdeDebuggerBreakMode    mode)
+{
+  LineInfo info;
+
+  g_return_if_fail (IDE_IS_DEBUGGER_BREAKPOINTS (self));
+
+  if (self->lines != NULL)
+    {
+      for (guint i = 0; i < self->lines->len; i++)
+        {
+          LineInfo *ele = &g_array_index (self->lines, LineInfo, i);
+
+          if (ele->line == line)
+            {
+              g_array_remove_index_fast (self->lines, i);
+              break;
+            }
+        }
+    }
+
+  if (mode == IDE_DEBUGGER_BREAK_NONE)
+    return;
+
+  if (self->lines == NULL)
+    self->lines = g_array_new (FALSE, FALSE, sizeof (LineInfo));
+
+  info.line = line;
+  info.mode = mode;
+
+  g_array_append_val (self->lines, info);
+  g_array_sort (self->lines, line_info_compare);
+}
diff --git a/libide/debugger/ide-debugger-breakpoints.h b/libide/debugger/ide-debugger-breakpoints.h
new file mode 100644
index 0000000..1db82c4
--- /dev/null
+++ b/libide/debugger/ide-debugger-breakpoints.h
@@ -0,0 +1,37 @@
+/* ide-debugger-breakpoints.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/>.
+ */
+
+#pragma once
+
+#include <glib-object.h>
+
+#include "ide-debugger-types.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_DEBUGGER_BREAKPOINTS (ide_debugger_breakpoints_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeDebuggerBreakpoints, ide_debugger_breakpoints, IDE, DEBUGGER_BREAKPOINTS, GObject)
+
+IdeDebuggerBreakMode ide_debugger_breakpoints_get_line (IdeDebuggerBreakpoints *self,
+                                                        guint                   line);
+void                 ide_debugger_breakpoints_set_line (IdeDebuggerBreakpoints *self,
+                                                        guint                   line,
+                                                        IdeDebuggerBreakMode    mode);
+
+G_END_DECLS
diff --git a/libide/debugger/ide-debugger-controls.c b/libide/debugger/ide-debugger-controls.c
new file mode 100644
index 0000000..71295b0
--- /dev/null
+++ b/libide/debugger/ide-debugger-controls.c
@@ -0,0 +1,40 @@
+/* ide-debugger-controls.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/>.
+ */
+
+#include "ide-debugger-controls.h"
+
+struct _IdeDebuggerControls
+{
+  GtkBin parent_instance;
+};
+
+G_DEFINE_TYPE (IdeDebuggerControls, ide_debugger_controls, GTK_TYPE_REVEALER)
+
+static void
+ide_debugger_controls_class_init (IdeDebuggerControlsClass *klass)
+{
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/builder/ui/ide-debugger-controls.ui");
+}
+
+static void
+ide_debugger_controls_init (IdeDebuggerControls *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+}
diff --git a/libide/debugger/ide-debugger-controls.h b/libide/debugger/ide-debugger-controls.h
new file mode 100644
index 0000000..3e7bd2d
--- /dev/null
+++ b/libide/debugger/ide-debugger-controls.h
@@ -0,0 +1,40 @@
+/* ide-debugger-controls.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_CONTROLS_H
+#define IDE_DEBUGGER_CONTROLS_H
+
+#include <gtk/gtk.h>
+
+#include "ide-types.h"
+
+#include "debugger/ide-debugger.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_DEBUGGER_CONTROLS (ide_debugger_controls_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeDebuggerControls, ide_debugger_controls, IDE, DEBUGGER_CONTROLS, GtkRevealer)
+
+IdeDebugger *ide_debugger_controls_get_debugger (IdeDebuggerControls *self);
+void         ide_debugger_controls_set_debugger (IdeDebuggerControls *self,
+                                                 IdeDebugger         *debugger);
+
+G_END_DECLS
+
+#endif /* IDE_DEBUGGER_CONTROLS_H */
diff --git a/libide/debugger/ide-debugger-controls.ui b/libide/debugger/ide-debugger-controls.ui
new file mode 100644
index 0000000..33a6cd9
--- /dev/null
+++ b/libide/debugger/ide-debugger-controls.ui
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="IdeDebuggerControls" parent="GtkRevealer">
+    <child>
+      <object class="GtkBox">
+        <property name="orientation">horizontal</property>
+        <property name="visible">true</property>
+        <style>
+          <class name="linked"/>
+        </style>
+        <child>
+          <object class="GtkButton" id="continue_button">
+            <property name="action-name">debug-manager.continue</property>
+            <property name="tooltip-text" translatable="yes">Continue running the program</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkImage">
+                <property name="icon-name">debug-continue-symbolic</property>
+                <property name="visible">true</property>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkButton" id="execute_from_cursor_button">
+            <property name="action-name">debug-manager.execute-from-cursor</property>
+            <property name="tooltip-text" translatable="yes">Execute from the current cursor 
position</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkImage">
+                <property name="icon-name">debug-execute-from-cursor-symbolic</property>
+                <property name="visible">true</property>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkButton" id="execute_to_cursor_button">
+            <property name="action-name">debug-manager.execute-to-cursor</property>
+            <property name="tooltip-text" translatable="yes">Execute until reaching the current cursor 
position</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkImage">
+                <property name="icon-name">debug-execute-to-cursor-symbolic</property>
+                <property name="visible">true</property>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkButton" id="step_in_button">
+            <property name="action-name">debug-manager.step-in</property>
+            <property name="tooltip-text" translatable="yes">Execute the current line, stepping into any 
function calls</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkImage">
+                <property name="icon-name">debug-step-in-symbolic</property>
+                <property name="visible">true</property>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkButton" id="step_over_button">
+            <property name="action-name">debug-manager.step-over</property>
+            <property name="tooltip-text" translatable="yes">Execute the current line, stepping over any 
function calls</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkImage">
+                <property name="icon-name">debug-step-over-symbolic</property>
+                <property name="visible">true</property>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkButton" id="finish_button">
+            <property name="action-name">debug-manager.finish</property>
+            <property name="tooltip-text" translatable="yes">Run until the end of the function</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkImage">
+                <property name="icon-name">debug-step-out-symbolic</property>
+                <property name="visible">true</property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/libide/debugger/ide-debugger-gutter-renderer.c b/libide/debugger/ide-debugger-gutter-renderer.c
new file mode 100644
index 0000000..89d3aac
--- /dev/null
+++ b/libide/debugger/ide-debugger-gutter-renderer.c
@@ -0,0 +1,288 @@
+/* ide-debugger-gutter-renderer.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-gutter-renderer"
+
+#include <glib/gi18n.h>
+
+#include "ide-debug.h"
+
+#include "debugger/ide-debugger-gutter-renderer.h"
+#include "debugger/ide-debugger-breakpoints.h"
+
+struct _IdeDebuggerGutterRenderer
+{
+  GtkSourceGutterRendererPixbuf  parent_instance;
+  IdeDebuggerBreakpoints        *breakpoints;
+  gulong                         breakpoints_changed_handler;
+};
+
+G_DEFINE_TYPE (IdeDebuggerGutterRenderer, ide_debugger_gutter_renderer, 
GTK_SOURCE_TYPE_GUTTER_RENDERER_PIXBUF)
+
+enum {
+  PROP_0,
+  PROP_BREAKPOINTS,
+  N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+ide_debugger_gutter_renderer_activate (IdeDebuggerGutterRenderer *self,
+                                       const GtkTextIter         *iter,
+                                       GdkRectangle              *area,
+                                       GdkEvent                  *event)
+{
+  IdeDebuggerBreakMode break_type;
+  guint line;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_DEBUGGER_GUTTER_RENDERER (self));
+  g_assert (iter != NULL);
+  g_assert (area != NULL);
+  g_assert (event != NULL);
+
+  line = gtk_text_iter_get_line (iter) + 1;
+
+  IDE_TRACE_MSG ("Toggle breakpoint on line %u", line);
+
+  break_type = ide_debugger_breakpoints_get_line (self->breakpoints, line);
+
+  switch (break_type)
+    {
+    case IDE_DEBUGGER_BREAK_NONE:
+      /* TOOD: Register with real debug manager */
+      ide_debugger_breakpoints_set_line (self->breakpoints, line, IDE_DEBUGGER_BREAK_BREAKPOINT);
+      break;
+
+    case IDE_DEBUGGER_BREAK_BREAKPOINT:
+    case IDE_DEBUGGER_BREAK_COUNTPOINT:
+    case IDE_DEBUGGER_BREAK_WATCHPOINT:
+      /* TOOD: Register with real debug manager */
+      ide_debugger_breakpoints_set_line (self->breakpoints, line, 0);
+      break;
+
+    default:
+      break;
+    }
+
+  IDE_EXIT;
+}
+
+static gboolean
+ide_debugger_gutter_renderer_query_activatable (IdeDebuggerGutterRenderer *self,
+                                                const GtkTextIter         *begin,
+                                                const GdkRectangle        *area,
+                                                GdkEvent                  *event)
+{
+  g_assert (IDE_IS_DEBUGGER_GUTTER_RENDERER (self));
+  g_assert (begin != NULL);
+  g_assert (area != NULL);
+  g_assert (event != NULL);
+
+  return TRUE;
+}
+
+static void
+ide_debugger_gutter_renderer_query_data (IdeDebuggerGutterRenderer    *self,
+                                         const GtkTextIter            *begin,
+                                         const GtkTextIter            *end,
+                                         GtkSourceGutterRendererState  state)
+{
+  IdeDebuggerBreakMode break_type;
+  guint line;
+
+  g_assert (IDE_IS_DEBUGGER_GUTTER_RENDERER (self));
+  g_assert (begin != NULL);
+  g_assert (end != NULL);
+
+  if (self->breakpoints == NULL)
+    return;
+
+  line = gtk_text_iter_get_line (begin) + 1;
+  break_type = ide_debugger_breakpoints_get_line (self->breakpoints, line);
+
+  /*
+   * These are very much a miss-appropriation of the icon, but it works
+   * well enough for now until we get real symbolic icons for these.
+   */
+#define BREAKPOINT_ICON_NAME "edit-clear-symbolic-rtl"
+#define COUNTPOINT_ICON_NAME "edit-clear-symbolic-rtl"
+#define WATCHPOINT_ICON_NAME "edit-clear-symbolic-rtl"
+
+  switch (break_type)
+    {
+    case IDE_DEBUGGER_BREAK_BREAKPOINT:
+      gtk_source_gutter_renderer_pixbuf_set_icon_name (GTK_SOURCE_GUTTER_RENDERER_PIXBUF (self), 
BREAKPOINT_ICON_NAME);
+      break;
+
+    case IDE_DEBUGGER_BREAK_COUNTPOINT:
+      gtk_source_gutter_renderer_pixbuf_set_icon_name (GTK_SOURCE_GUTTER_RENDERER_PIXBUF (self), 
COUNTPOINT_ICON_NAME);
+      break;
+
+    case IDE_DEBUGGER_BREAK_WATCHPOINT:
+      gtk_source_gutter_renderer_pixbuf_set_icon_name (GTK_SOURCE_GUTTER_RENDERER_PIXBUF (self), 
WATCHPOINT_ICON_NAME);
+      break;
+
+    case IDE_DEBUGGER_BREAK_NONE:
+    default:
+      /* Setting pixbuf to NULL via g_object_set() seems to be
+       * the only way to clear this without g_warning()s.
+       */
+      g_object_set (self, "pixbuf", NULL, NULL);
+      break;
+    }
+
+#undef BREAKPOINT_ICON_NAME
+#undef COUNTPOINT_ICON_NAME
+#undef WATCHPOINT_ICON_NAME
+}
+
+static void
+ide_debugger_gutter_renderer_breakpoints_changed (IdeDebuggerGutterRenderer *self,
+                                                  IdeDebuggerBreakpoints    *breakpoints)
+{
+  g_assert (IDE_IS_DEBUGGER_GUTTER_RENDERER (self));
+  g_assert (IDE_IS_DEBUGGER_BREAKPOINTS (breakpoints));
+
+  gtk_source_gutter_renderer_queue_draw (GTK_SOURCE_GUTTER_RENDERER (self));
+}
+
+void
+ide_debugger_gutter_renderer_set_breakpoints (IdeDebuggerGutterRenderer *self,
+                                              IdeDebuggerBreakpoints    *breakpoints)
+{
+  g_return_if_fail (IDE_IS_DEBUGGER_GUTTER_RENDERER (self));
+  g_return_if_fail (!breakpoints || IDE_IS_DEBUGGER_BREAKPOINTS (breakpoints));
+
+  if (self->breakpoints != breakpoints)
+    {
+      if (self->breakpoints != NULL)
+        {
+          g_signal_handler_disconnect (self->breakpoints, self->breakpoints_changed_handler);
+          self->breakpoints_changed_handler = 0;
+          g_clear_object (&self->breakpoints);
+        }
+
+      if (breakpoints != NULL)
+        {
+          self->breakpoints = g_object_ref (breakpoints);
+          self->breakpoints_changed_handler =
+            g_signal_connect_object (breakpoints,
+                                     "changed",
+                                     G_CALLBACK (ide_debugger_gutter_renderer_breakpoints_changed),
+                                     self,
+                                     G_CONNECT_SWAPPED);
+        }
+
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BREAKPOINTS]);
+    }
+}
+
+static void
+ide_debugger_gutter_renderer_finalize (GObject *object)
+{
+  IdeDebuggerGutterRenderer *self = (IdeDebuggerGutterRenderer *)object;
+
+  g_clear_object (&self->breakpoints);
+
+  G_OBJECT_CLASS (ide_debugger_gutter_renderer_parent_class)->finalize (object);
+}
+
+static void
+ide_debugger_gutter_renderer_get_property (GObject    *object,
+                                           guint       prop_id,
+                                           GValue     *value,
+                                           GParamSpec *pspec)
+{
+  IdeDebuggerGutterRenderer *self = IDE_DEBUGGER_GUTTER_RENDERER (object);
+
+  switch (prop_id)
+    {
+    case PROP_BREAKPOINTS:
+      g_value_set_object (value, self->breakpoints);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_debugger_gutter_renderer_set_property (GObject      *object,
+                                           guint         prop_id,
+                                           const GValue *value,
+                                           GParamSpec   *pspec)
+{
+  IdeDebuggerGutterRenderer *self = IDE_DEBUGGER_GUTTER_RENDERER (object);
+
+  switch (prop_id)
+    {
+    case PROP_BREAKPOINTS:
+      ide_debugger_gutter_renderer_set_breakpoints (self, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_debugger_gutter_renderer_class_init (IdeDebuggerGutterRendererClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_debugger_gutter_renderer_finalize;
+  object_class->get_property = ide_debugger_gutter_renderer_get_property;
+  object_class->set_property = ide_debugger_gutter_renderer_set_property;
+
+  properties [PROP_BREAKPOINTS] =
+    g_param_spec_object ("breakpoints",
+                         "Breakpoints",
+                         "Breakpoints",
+                         IDE_TYPE_DEBUGGER_BREAKPOINTS,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+ide_debugger_gutter_renderer_init (IdeDebuggerGutterRenderer *self)
+{
+  g_signal_connect (self,
+                    "activate",
+                    G_CALLBACK (ide_debugger_gutter_renderer_activate),
+                    NULL);
+
+  g_signal_connect (self,
+                    "query-activatable",
+                    G_CALLBACK (ide_debugger_gutter_renderer_query_activatable),
+                    NULL);
+
+  g_signal_connect (self,
+                    "query-data",
+                    G_CALLBACK (ide_debugger_gutter_renderer_query_data),
+                    NULL);
+}
+
+GtkSourceGutterRenderer *
+ide_debugger_gutter_renderer_new (void)
+{
+  return g_object_new (IDE_TYPE_DEBUGGER_GUTTER_RENDERER, NULL);
+}
diff --git a/libide/debugger/ide-debugger-gutter-renderer.h b/libide/debugger/ide-debugger-gutter-renderer.h
new file mode 100644
index 0000000..dd11d35
--- /dev/null
+++ b/libide/debugger/ide-debugger-gutter-renderer.h
@@ -0,0 +1,37 @@
+/* ide-debugger-gutter-renderer.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/>.
+ */
+
+#pragma once
+
+#include <gtksourceview/gtksource.h>
+
+#include "ide-types.h"
+
+#include "debugger/ide-debugger-breakpoints.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_DEBUGGER_GUTTER_RENDERER (ide_debugger_gutter_renderer_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeDebuggerGutterRenderer, ide_debugger_gutter_renderer, IDE, 
DEBUGGER_GUTTER_RENDERER, GtkSourceGutterRendererPixbuf)
+
+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-libraries-view.ui b/libide/debugger/ide-debugger-libraries-view.ui
new file mode 100644
index 0000000..e8dd3fb
--- /dev/null
+++ b/libide/debugger/ide-debugger-libraries-view.ui
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="IdeDebuggerLibrariesView" parent="GtkBin">
+    <child>
+      <object class="GtkScrolledWindow">
+        <property name="visible">true</property>
+        <child>
+          <object class="GtkTreeView" id="tree_view">
+            <property name="model">list_store</property>
+            <property name="sensitive">false</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkTreeViewColumn" id="target_column">
+                <property name="title" translatable="yes">Library</property>
+                <property name="expand">true</property>
+                <child>
+                  <object class="GtkCellRendererText" id="target_cell">
+                    <property name="ellipsize">start</property>
+                    <property name="xalign">0.0</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkTreeViewColumn" id="range_column">
+                <property name="title" translatable="yes">Address Range</property>
+                <child>
+                  <object class="GtkCellRendererText" id="range_cell">
+                    <property name="xalign">0.0</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+  <object class="GtkListStore" id="list_store">
+    <columns>
+      <column type="IdeDebuggerLibrary"/>
+    </columns>
+  </object>
+</interface>
diff --git a/libide/debugger/ide-debugger-locals-view.ui b/libide/debugger/ide-debugger-locals-view.ui
new file mode 100644
index 0000000..2c85151
--- /dev/null
+++ b/libide/debugger/ide-debugger-locals-view.ui
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="IdeDebuggerLocalsView" parent="GtkBin">
+    <child>
+      <object class="GtkScrolledWindow">
+        <property name="min-content-width">300</property>
+        <property name="visible">true</property>
+        <child>
+          <object class="GtkTreeView" id="tree_view">
+            <property name="model">tree_store</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkTreeViewColumn" id="variable_column">
+                <property name="expand">true</property>
+                <property name="resizable">true</property>
+                <property name="title" translatable="yes">Variable</property>
+                <child>
+                  <object class="GtkCellRendererText" id="variable_cell">
+                    <property name="xalign">0.0</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkTreeViewColumn" id="type_column">
+                <property name="expand">true</property>
+                <property name="resizable">true</property>
+                <property name="title" translatable="yes">Type</property>
+                <child>
+                  <object class="GtkCellRendererText" id="type_cell">
+                    <property name="ellipsize">end</property>
+                    <property name="xalign">0.0</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkTreeViewColumn" id="value_column">
+                <property name="expand">true</property>
+                <property name="resizable">true</property>
+                <property name="title" translatable="yes">Value</property>
+                <child>
+                  <object class="GtkCellRendererText" id="value_cell">
+                    <property name="ellipsize">end</property>
+                    <property name="xalign">0.0</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+  <object class="GtkTreeStore" id="tree_store">
+    <columns>
+      <column type="GObject"/>
+      <column type="gchararray"/>
+    </columns>
+  </object>
+</interface>
diff --git a/libide/debugger/ide-debugger-perspective.c b/libide/debugger/ide-debugger-perspective.c
new file mode 100644
index 0000000..e9376c6
--- /dev/null
+++ b/libide/debugger/ide-debugger-perspective.c
@@ -0,0 +1,376 @@
+/* ide-debugger-perspective.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-perspective"
+
+#include <dazzle.h>
+#include <glib/gi18n.h>
+
+#include "ide-debug.h"
+
+#include "buffers/ide-buffer.h"
+#include "debugger/ide-debugger.h"
+#include "debugger/ide-debugger-breakpoints-view.h"
+#include "debugger/ide-debugger-locals-view.h"
+#include "debugger/ide-debugger-perspective.h"
+#include "debugger/ide-debugger-registers-view.h"
+#include "debugger/ide-debugger-threads-view.h"
+#include "debugger/ide-debugger-view.h"
+#include "layout/ide-layout-grid.h"
+#include "workbench/ide-perspective.h"
+
+struct _IdeDebuggerPerspective
+{
+  IdeLayout       parent_instance;
+
+  /* Owned references */
+  IdeDebugger    *debugger;
+  DzlSignalGroup *debugger_signals;
+  GSettings      *terminal_settings;
+  GtkCssProvider *log_css;
+
+  /* Template references */
+  GtkTextBuffer  *log_buffer;
+  GtkTextView    *log_text_view;
+  IdeLayoutGrid  *layout_grid;
+};
+
+enum {
+  PROP_0,
+  PROP_DEBUGGER,
+  N_PROPS
+};
+
+static gchar *
+ide_debugger_perspective_get_title (IdePerspective *perspective)
+{
+  return g_strdup (_("Debugger"));
+}
+
+static gchar *
+ide_debugger_perspective_get_id (IdePerspective *perspective)
+{
+  return g_strdup ("debugger");
+}
+
+static gchar *
+ide_debugger_perspective_get_icon_name (IdePerspective *perspective)
+{
+  return g_strdup ("builder-debugger-symbolic");
+}
+
+static gchar *
+ide_debugger_perspective_get_accelerator (IdePerspective *perspective)
+{
+  return g_strdup ("<Alt>2");
+}
+
+static void
+perspective_iface_init (IdePerspectiveInterface *iface)
+{
+  iface->get_accelerator = ide_debugger_perspective_get_accelerator;
+  iface->get_icon_name = ide_debugger_perspective_get_icon_name;
+  iface->get_id = ide_debugger_perspective_get_id;
+  iface->get_title = ide_debugger_perspective_get_title;
+}
+
+G_DEFINE_TYPE_WITH_CODE  (IdeDebuggerPerspective, ide_debugger_perspective, IDE_TYPE_LAYOUT,
+                          G_IMPLEMENT_INTERFACE (IDE_TYPE_PERSPECTIVE, perspective_iface_init))
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+on_debugger_log (IdeDebuggerPerspective *self,
+                 IdeDebuggerStream       stream,
+                 GBytes                 *content,
+                 IdeDebugger            *debugger)
+{
+  g_assert (IDE_IS_DEBUGGER_PERSPECTIVE (self));
+  g_assert (IDE_IS_DEBUGGER_STREAM (stream));
+  g_assert (IDE_IS_DEBUGGER (debugger));
+
+  if (stream == IDE_DEBUGGER_CONSOLE)
+    {
+      const gchar *str;
+      GtkTextIter iter;
+      gsize len;
+
+      str = (gchar *)g_bytes_get_data (content, &len);
+
+      gtk_text_buffer_get_end_iter (self->log_buffer, &iter);
+      gtk_text_buffer_insert (self->log_buffer, &iter, str, len);
+      gtk_text_buffer_select_range (self->log_buffer, &iter, &iter);
+      gtk_text_view_scroll_to_iter (self->log_text_view, &iter, 0.0, FALSE, 1.0, 1.0);
+    }
+}
+
+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));
+
+  if (g_set_object (&self->debugger, debugger))
+    {
+      dzl_signal_group_set_target (self->debugger_signals, debugger);
+      gtk_text_buffer_set_text (self->log_buffer, "", 0);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DEBUGGER]);
+    }
+
+  IDE_EXIT;
+}
+
+static void
+log_panel_changed_font_name (IdeDebuggerPerspective *self,
+                             const gchar            *key,
+                             GSettings              *settings)
+{
+  gchar *font_name;
+  PangoFontDescription *font_desc;
+
+  g_assert (IDE_IS_DEBUGGER_PERSPECTIVE (self));
+  g_assert (g_strcmp0 (key, "font-name") == 0);
+  g_assert (G_IS_SETTINGS (settings));
+
+  font_name = g_settings_get_string (settings, key);
+  font_desc = pango_font_description_from_string (font_name);
+
+  if (font_desc != NULL)
+    {
+      gchar *fragment;
+      gchar *css;
+
+      fragment = dzl_pango_font_description_to_css (font_desc);
+      css = g_strdup_printf ("textview { %s }", fragment);
+
+      gtk_css_provider_load_from_data (self->log_css, css, -1, NULL);
+
+      pango_font_description_free (font_desc);
+      g_free (fragment);
+      g_free (css);
+    }
+
+  g_free (font_name);
+}
+
+static void
+on_debugger_stopped (IdeDebuggerPerspective *self,
+                     IdeDebuggerStopReason   reason,
+                     IdeDebuggerBreakpoint  *breakpoint,
+                     IdeDebugger            *debugger)
+{
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_DEBUGGER_PERSPECTIVE (self));
+  g_assert (!breakpoint || IDE_IS_DEBUGGER_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;
+
+  g_clear_object (&self->debugger);
+  g_clear_object (&self->debugger_signals);
+  g_clear_object (&self->terminal_settings);
+  g_clear_object (&self->log_css);
+
+  G_OBJECT_CLASS (ide_debugger_perspective_parent_class)->finalize (object);
+}
+
+static void
+ide_debugger_perspective_get_property (GObject    *object,
+                                       guint       prop_id,
+                                       GValue     *value,
+                                       GParamSpec *pspec)
+{
+  IdeDebuggerPerspective *self = IDE_DEBUGGER_PERSPECTIVE (object);
+
+  switch (prop_id)
+    {
+    case PROP_DEBUGGER:
+      g_value_set_object (value, self->debugger);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_debugger_perspective_set_property (GObject      *object,
+                                       guint         prop_id,
+                                       const GValue *value,
+                                       GParamSpec   *pspec)
+{
+  IdeDebuggerPerspective *self = IDE_DEBUGGER_PERSPECTIVE (object);
+
+  switch (prop_id)
+    {
+    case PROP_DEBUGGER:
+      ide_debugger_perspective_set_debugger (self, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_debugger_perspective_class_init (IdeDebuggerPerspectiveClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->finalize = ide_debugger_perspective_finalize;
+  object_class->get_property = ide_debugger_perspective_get_property;
+  object_class->set_property = ide_debugger_perspective_set_property;
+
+  properties [PROP_DEBUGGER] =
+    g_param_spec_object ("debugger",
+                         "Debugger",
+                         "The current debugger instance",
+                         IDE_TYPE_DEBUGGER,
+                         (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-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);
+
+  g_type_ensure (IDE_TYPE_DEBUGGER_BREAKPOINTS_VIEW);
+  g_type_ensure (IDE_TYPE_DEBUGGER_LOCALS_VIEW);
+  g_type_ensure (IDE_TYPE_DEBUGGER_REGISTERS_VIEW);
+  g_type_ensure (IDE_TYPE_DEBUGGER_THREADS_VIEW);
+}
+
+static void
+ide_debugger_perspective_init (IdeDebuggerPerspective *self)
+{
+  GtkStyleContext *context;
+
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  self->debugger_signals = dzl_signal_group_new (IDE_TYPE_DEBUGGER);
+
+  dzl_signal_group_connect_object (self->debugger_signals,
+                                   "log",
+                                   G_CALLBACK (on_debugger_log),
+                                   self,
+                                   G_CONNECT_SWAPPED);
+
+  dzl_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,
+                                  GTK_STYLE_PROVIDER (self->log_css),
+                                  GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+
+  self->terminal_settings = g_settings_new ("org.gnome.builder.terminal");
+  g_signal_connect_object (self->terminal_settings,
+                           "changed::font-name",
+                           G_CALLBACK (log_panel_changed_font_name),
+                           self,
+                           G_CONNECT_SWAPPED);
+  log_panel_changed_font_name (self, "font-name", self->terminal_settings);
+}
+
+static void
+ide_debugger_perspective_locate_by_file (GtkWidget *widget,
+                                         gpointer   user_data)
+{
+  struct {
+    const gchar     *file;
+    IdeDebuggerView *view;
+  } *lookup = user_data;
+
+  if (lookup->view != NULL)
+    return;
+
+  if (IDE_IS_DEBUGGER_VIEW (widget))
+    {
+    }
+}
+
+void
+ide_debugger_perspective_navigate_to_breakpoint (IdeDebuggerPerspective *self,
+                                                 IdeDebuggerBreakpoint  *breakpoint)
+{
+  struct {
+    const gchar     *file;
+    IdeDebuggerView *view;
+  } lookup = { 0 };
+
+  IDE_ENTRY;
+
+  g_return_if_fail (IDE_IS_DEBUGGER_PERSPECTIVE (self));
+  g_return_if_fail (IDE_IS_DEBUGGER_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.
+   */
+
+  lookup.file = ide_debugger_breakpoint_get_file (breakpoint);
+  g_return_if_fail (lookup.file != NULL);
+
+  ide_layout_grid_foreach_view (self->layout_grid,
+                                ide_debugger_perspective_locate_by_file,
+                                &lookup);
+
+  if (lookup.view != NULL)
+    {
+      //ide_debugger_view_scroll_to_line (lookup.view,
+                                        //ide_debugger_breakpoint_get_line (breakpoint));
+      gtk_widget_grab_focus (GTK_WIDGET (lookup.view));
+      return;
+    }
+
+  g_print ("Need to load source\n");
+
+  IDE_EXIT;
+}
diff --git a/libide/debugger/ide-debugger-perspective.h b/libide/debugger/ide-debugger-perspective.h
new file mode 100644
index 0000000..d83075e
--- /dev/null
+++ b/libide/debugger/ide-debugger-perspective.h
@@ -0,0 +1,35 @@
+/* ide-debugger-perspective.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/>.
+ */
+
+#pragma once
+
+#include "debugger/ide-debugger-breakpoint.h"
+#include "layout/ide-layout.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_DEBUGGER_PERSPECTIVE (ide_debugger_perspective_get_type())
+
+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,
+                                                      IdeDebuggerBreakpoint  *breakpoint);
+
+G_END_DECLS
diff --git a/libide/debugger/ide-debugger-perspective.ui b/libide/debugger/ide-debugger-perspective.ui
new file mode 100644
index 0000000..67f676a
--- /dev/null
+++ b/libide/debugger/ide-debugger-perspective.ui
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="IdeDebuggerPerspective" parent="IdeLayout">
+    <child>
+      <object class="IdeLayoutGrid" id="layout_grid">
+        <property name="visible">true</property>
+      </object>
+    </child>
+    <child internal-child="bottom">
+      <object class="DzlDockBinEdge">
+        <property name="reveal-child">true</property>
+      </object>
+    </child>
+    <child type="bottom">
+      <object class="DzlDockStack">
+        <property name="hexpand">true</property>
+        <property name="visible">true</property>
+        <child>
+          <object class="DzlDockWidget">
+            <property name="title" translatable="yes">Application Output</property>
+            <property name="expand">true</property>
+            <property name="visible">true</property>
+          </object>
+        </child>
+        <child>
+          <object class="DzlDockWidget">
+            <property name="title" translatable="yes">Breakpoints</property>
+            <property name="expand">true</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="IdeDebuggerBreakpointsView" id="breakpoints_view">
+                <property name="visible">true</property>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="DzlDockWidget">
+            <property name="title" translatable="yes">Threads</property>
+            <property name="expand">true</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="DzlMultiPaned">
+                <property name="orientation">horizontal</property>
+                <property name="visible">true</property>
+                <child>
+                  <object class="IdeDebuggerThreadsView" id="threads_view">
+                    <property name="hexpand">true</property>
+                    <property name="visible">true</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="IdeDebuggerLocalsView" id="locals_view">
+                    <property name="hexpand">true</property>
+                    <property name="visible">true</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="DzlDockWidget">
+            <property name="title" translatable="yes">Registers</property>
+            <property name="expand">true</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="IdeDebuggerRegistersView" id="registers_view">
+                <property name="visible">true</property>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="DzlDockWidget">
+            <property name="title" translatable="yes">Libraries</property>
+            <property name="expand">true</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="IdeDebuggerLibrariesView" id="libraries_view">
+                <property name="visible">true</property>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="DzlDockWidget">
+            <property name="title" translatable="yes">Log</property>
+            <property name="expand">true</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkScrolledWindow">
+                <child>
+                  <object class="GtkTextView" id="log_text_view">
+                    <property name="buffer">log_buffer</property>
+                    <property name="visible">true</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+  <object class="GtkTextBuffer" id="log_buffer">
+  </object>
+</interface>
diff --git a/libide/debugger/ide-debugger-plugin.c b/libide/debugger/ide-debugger-plugin.c
new file mode 100644
index 0000000..e45cc7f
--- /dev/null
+++ b/libide/debugger/ide-debugger-plugin.c
@@ -0,0 +1,40 @@
+/* ide-debugger-plugin.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/>.
+ */
+
+#include <libpeas/peas.h>
+
+#if 0
+#include "debugger/ide-debugger-editor-view-addin.h"
+#endif
+#include "debugger/ide-debugger-workbench-addin.h"
+#include "editor/ide-editor-view-addin.h"
+#include "workbench/ide-workbench-addin.h"
+
+void
+ide_debugger_register_types (PeasObjectModule *module)
+{
+#if 0
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_EDITOR_VIEW_ADDIN,
+                                              IDE_TYPE_DEBUGGER_EDITOR_VIEW_ADDIN);
+#endif
+
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_WORKBENCH_ADDIN,
+                                              IDE_TYPE_DEBUGGER_WORKBENCH_ADDIN);
+}
diff --git a/libide/debugger/ide-debugger-private.h b/libide/debugger/ide-debugger-private.h
index 3641bf3..fd20f5b 100644
--- a/libide/debugger/ide-debugger-private.h
+++ b/libide/debugger/ide-debugger-private.h
@@ -18,78 +18,82 @@
 
 #pragma once
 
+#include "ide-debug-manager.h"
 #include "ide-debugger.h"
+#include "ide-debugger-breakpoints.h"
 
 G_BEGIN_DECLS
 
-void       _ide_debugger_class_init_actions            (GActionGroupInterface          *iface);
-void       _ide_debugger_update_actions                (IdeDebugger                    *self);
-gboolean   _ide_debugger_get_has_started               (IdeDebugger                    *self);
-void       _ide_debugger_real_list_frames_async        (IdeDebugger                    *self,
-                                                        IdeDebuggerThread              *thread,
-                                                        GCancellable                   *cancellable,
-                                                        GAsyncReadyCallback             callback,
-                                                        gpointer                        user_data);
-GPtrArray *_ide_debugger_real_list_frames_finish       (IdeDebugger                    *self,
-                                                        GAsyncResult                   *result,
-                                                        GError                        **error);
-void       _ide_debugger_real_interrupt_async          (IdeDebugger                    *self,
-                                                        IdeDebuggerThreadGroup         *thread_group,
-                                                        GCancellable                   *cancellable,
-                                                        GAsyncReadyCallback             callback,
-                                                        gpointer                        user_data);
-gboolean   _ide_debugger_real_interrupt_finish         (IdeDebugger                    *self,
-                                                        GAsyncResult                   *result,
-                                                        GError                        **error);
-void       _ide_debugger_real_send_signal_async        (IdeDebugger                    *self,
-                                                        gint                            signum,
-                                                        GCancellable                   *cancellable,
-                                                        GAsyncReadyCallback             callback,
-                                                        gpointer                        user_data);
-gboolean   _ide_debugger_real_send_signal_finish       (IdeDebugger                    *self,
-                                                        GAsyncResult                   *result,
-                                                        GError                        **error);
-void       _ide_debugger_real_modify_breakpoint_async  (IdeDebugger                    *self,
-                                                        IdeDebuggerBreakpointChange     change,
-                                                        IdeDebuggerBreakpoint          *breakpoint,
-                                                        GCancellable                   *cancellable,
-                                                        GAsyncReadyCallback             callback,
-                                                        gpointer                        user_data);
-gboolean   _ide_debugger_real_modify_breakpoint_finish (IdeDebugger                    *self,
-                                                        GAsyncResult                   *result,
-                                                        GError                        **error);
-void       _ide_debugger_real_list_params_async        (IdeDebugger                    *self,
-                                                        IdeDebuggerThread              *thread,
-                                                        IdeDebuggerFrame               *frame,
-                                                        GCancellable                   *cancellable,
-                                                        GAsyncReadyCallback             callback,
-                                                        gpointer                        user_data);
-GPtrArray *_ide_debugger_real_list_params_finish       (IdeDebugger                    *self,
-                                                        GAsyncResult                   *result,
-                                                        GError                        **error);
-void       _ide_debugger_real_list_locals_async        (IdeDebugger                    *self,
-                                                        IdeDebuggerThread              *thread,
-                                                        IdeDebuggerFrame               *frame,
-                                                        GCancellable                   *cancellable,
-                                                        GAsyncReadyCallback             callback,
-                                                        gpointer                        user_data);
-GPtrArray *_ide_debugger_real_list_locals_finish       (IdeDebugger                    *self,
-                                                        GAsyncResult                   *result,
-                                                        GError                        **error);
-void       _ide_debugger_real_list_registers_async     (IdeDebugger                    *self,
-                                                        GCancellable                   *cancellable,
-                                                        GAsyncReadyCallback             callback,
-                                                        gpointer                        user_data);
-GPtrArray *_ide_debugger_real_list_registers_finish    (IdeDebugger                    *self,
-                                                        GAsyncResult                   *result,
-                                                        GError                        **error);
-void       _ide_debugger_real_disassemble_async        (IdeDebugger                    *self,
-                                                        const IdeDebuggerAddressRange  *range,
-                                                        GCancellable                   *cancellable,
-                                                        GAsyncReadyCallback             callback,
-                                                        gpointer                        user_data);
-GPtrArray *_ide_debugger_real_disassemble_finish       (IdeDebugger                    *self,
-                                                        GAsyncResult                   *result,
-                                                        GError                        **error);
+IdeDebuggerBreakpoints *_ide_debug_manager_get_breakpoints          (IdeDebugManager                *self,
+                                                                     GFile                          *file);
+void                    _ide_debugger_class_init_actions            (GActionGroupInterface          *iface);
+void                    _ide_debugger_update_actions                (IdeDebugger                    *self);
+gboolean                _ide_debugger_get_has_started               (IdeDebugger                    *self);
+void                    _ide_debugger_real_list_frames_async        (IdeDebugger                    *self,
+                                                                     IdeDebuggerThread              *thread,
+                                                                     GCancellable                   
*cancellable,
+                                                                     GAsyncReadyCallback             
callback,
+                                                                     gpointer                        
user_data);
+GPtrArray              *_ide_debugger_real_list_frames_finish       (IdeDebugger                    *self,
+                                                                     GAsyncResult                   *result,
+                                                                     GError                        **error);
+void                    _ide_debugger_real_interrupt_async          (IdeDebugger                    *self,
+                                                                     IdeDebuggerThreadGroup         
*thread_group,
+                                                                     GCancellable                   
*cancellable,
+                                                                     GAsyncReadyCallback             
callback,
+                                                                     gpointer                        
user_data);
+gboolean                _ide_debugger_real_interrupt_finish         (IdeDebugger                    *self,
+                                                                     GAsyncResult                   *result,
+                                                                     GError                        **error);
+void                    _ide_debugger_real_send_signal_async        (IdeDebugger                    *self,
+                                                                     gint                            signum,
+                                                                     GCancellable                   
*cancellable,
+                                                                     GAsyncReadyCallback             
callback,
+                                                                     gpointer                        
user_data);
+gboolean                _ide_debugger_real_send_signal_finish       (IdeDebugger                    *self,
+                                                                     GAsyncResult                   *result,
+                                                                     GError                        **error);
+void                    _ide_debugger_real_modify_breakpoint_async  (IdeDebugger                    *self,
+                                                                     IdeDebuggerBreakpointChange     change,
+                                                                     IdeDebuggerBreakpoint          
*breakpoint,
+                                                                     GCancellable                   
*cancellable,
+                                                                     GAsyncReadyCallback             
callback,
+                                                                     gpointer                        
user_data);
+gboolean                _ide_debugger_real_modify_breakpoint_finish (IdeDebugger                    *self,
+                                                                     GAsyncResult                   *result,
+                                                                     GError                        **error);
+void                    _ide_debugger_real_list_params_async        (IdeDebugger                    *self,
+                                                                     IdeDebuggerThread              *thread,
+                                                                     IdeDebuggerFrame               *frame,
+                                                                     GCancellable                   
*cancellable,
+                                                                     GAsyncReadyCallback             
callback,
+                                                                     gpointer                        
user_data);
+GPtrArray              *_ide_debugger_real_list_params_finish       (IdeDebugger                    *self,
+                                                                     GAsyncResult                   *result,
+                                                                     GError                        **error);
+void                    _ide_debugger_real_list_locals_async        (IdeDebugger                    *self,
+                                                                     IdeDebuggerThread              *thread,
+                                                                     IdeDebuggerFrame               *frame,
+                                                                     GCancellable                   
*cancellable,
+                                                                     GAsyncReadyCallback             
callback,
+                                                                     gpointer                        
user_data);
+GPtrArray              *_ide_debugger_real_list_locals_finish       (IdeDebugger                    *self,
+                                                                     GAsyncResult                   *result,
+                                                                     GError                        **error);
+void                    _ide_debugger_real_list_registers_async     (IdeDebugger                    *self,
+                                                                     GCancellable                   
*cancellable,
+                                                                     GAsyncReadyCallback             
callback,
+                                                                     gpointer                        
user_data);
+GPtrArray              *_ide_debugger_real_list_registers_finish    (IdeDebugger                    *self,
+                                                                     GAsyncResult                   *result,
+                                                                     GError                        **error);
+void                    _ide_debugger_real_disassemble_async        (IdeDebugger                    *self,
+                                                                     const IdeDebuggerAddressRange  *range,
+                                                                     GCancellable                   
*cancellable,
+                                                                     GAsyncReadyCallback             
callback,
+                                                                     gpointer                        
user_data);
+GPtrArray              *_ide_debugger_real_disassemble_finish       (IdeDebugger                    *self,
+                                                                     GAsyncResult                   *result,
+                                                                     GError                        **error);
 
 G_END_DECLS
diff --git a/libide/debugger/ide-debugger-registers-view.ui b/libide/debugger/ide-debugger-registers-view.ui
new file mode 100644
index 0000000..eab4a2b
--- /dev/null
+++ b/libide/debugger/ide-debugger-registers-view.ui
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="IdeDebuggerRegistersView" parent="GtkBin">
+    <child>
+      <object class="GtkScrolledWindow">
+        <property name="visible">true</property>
+        <child>
+          <object class="GtkTreeView" id="tree_view">
+            <property name="model">list_store</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkTreeViewColumn" id="id_column">
+                <property name="title" translatable="yes">ID</property>
+                <child>
+                  <object class="GtkCellRendererText" id="id_cell">
+                    <property name="xalign">0.0</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkTreeViewColumn" id="name_column">
+                <property name="title" translatable="yes">Register</property>
+                <child>
+                  <object class="GtkCellRendererText" id="name_cell">
+                    <property name="xalign">0.0</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkTreeViewColumn" id="value_column">
+                <property name="expand">true</property>
+                <property name="title" translatable="yes">Value</property>
+                <child>
+                  <object class="GtkCellRendererText" id="value_cell">
+                    <property name="xalign">0.0</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+  <object class="GtkListStore" id="list_store">
+    <columns>
+      <column type="IdeDebuggerRegister"/>
+    </columns>
+  </object>
+</interface>
diff --git a/libide/debugger/ide-debugger-threads-view.ui b/libide/debugger/ide-debugger-threads-view.ui
new file mode 100644
index 0000000..b81c8b4
--- /dev/null
+++ b/libide/debugger/ide-debugger-threads-view.ui
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="IdeDebuggerThreadsView" parent="GtkBin">
+    <child>
+      <object class="DzlMultiPaned">
+        <property name="orientation">horizontal</property>
+        <property name="visible">true</property>
+        <child>
+          <object class="GtkScrolledWindow">
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkTreeView" id="thread_groups_tree_view">
+                <property name="activate-on-single-click">true</property>
+                <property name="model">thread_groups_store</property>
+                <property name="visible">true</property>
+                <child>
+                  <object class="GtkTreeViewColumn" id="group_column">
+                    <property name="title" translatable="yes">Group</property>
+                    <child>
+                      <object class="GtkCellRendererText" id="group_cell">
+                        <property name="xalign">0.0</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkScrolledWindow">
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkTreeView" id="threads_tree_view">
+                <property name="activate-on-single-click">true</property>
+                <property name="model">threads_store</property>
+                <property name="visible">true</property>
+                <child>
+                  <object class="GtkTreeViewColumn" id="thread_column">
+                    <property name="title" translatable="yes">Thread</property>
+                    <child>
+                      <object class="GtkCellRendererText" id="thread_cell">
+                        <property name="xalign">0.0</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkScrolledWindow">
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkTreeView" id="frames_tree_view">
+                <property name="activate-on-single-click">true</property>
+                <property name="model">frames_store</property>
+                <property name="visible">true</property>
+                <child>
+                  <object class="GtkTreeViewColumn" id="depth_column">
+                    <property name="title" translatable="yes">Frame</property>
+                    <child>
+                      <object class="GtkCellRendererText" id="depth_cell">
+                        <property name="xalign">0.0</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkTreeViewColumn" id="function_column">
+                    <property name="expand">true</property>
+                    <property name="resizable">true</property>
+                    <property name="title" translatable="yes">Function</property>
+                    <child>
+                      <object class="GtkCellRendererText" id="function_cell">
+                        <property name="ellipsize">middle</property>
+                        <property name="xalign">0.0</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkTreeViewColumn" id="args_column">
+                    <property name="resizable">true</property>
+                    <property name="title" translatable="yes">Arguments</property>
+                    <child>
+                      <object class="GtkCellRendererText" id="args_cell">
+                        <property name="ellipsize">middle</property>
+                        <property name="xalign">0.0</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkTreeViewColumn" id="location_column">
+                    <property name="expand">true</property>
+                    <property name="resizable">true</property>
+                    <property name="title" translatable="yes">Location</property>
+                    <child>
+                      <object class="GtkCellRendererText" id="location_cell">
+                        <property name="ellipsize">start</property>
+                        <property name="xalign">0.0</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkTreeViewColumn" id="binary_column">
+                    <property name="title" translatable="yes">Binary</property>
+                    <child>
+                      <object class="GtkCellRendererText" id="binary_cell">
+                        <property name="xalign">0.0</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+  <object class="GtkListStore" id="thread_groups_store">
+    <columns>
+      <column type="IdeDebuggerThreadGroup"/>
+    </columns>
+  </object>
+  <object class="GtkListStore" id="threads_store">
+    <columns>
+      <column type="IdeDebuggerThread"/>
+    </columns>
+  </object>
+  <object class="GtkListStore" id="frames_store">
+    <columns>
+      <column type="IdeDebuggerFrame"/>
+    </columns>
+  </object>
+</interface>
diff --git a/libide/debugger/ide-debugger-types.c b/libide/debugger/ide-debugger-types.c
index 80cd7c5..47b92c1 100644
--- a/libide/debugger/ide-debugger-types.c
+++ b/libide/debugger/ide-debugger-types.c
@@ -102,6 +102,7 @@ ide_debugger_break_mode_get_type (void)
     {
       GType _type_id;
       static const GEnumValue values[] = {
+        { IDE_DEBUGGER_BREAK_NONE, "IDE_DEBUGGER_BREAK_NONE", "none" },
         { IDE_DEBUGGER_BREAK_BREAKPOINT, "IDE_DEBUGGER_BREAK_BREAKPOINT", "breakpoint" },
         { IDE_DEBUGGER_BREAK_COUNTPOINT, "IDE_DEBUGGER_BREAK_COUNTPOINT", "countpoint" },
         { IDE_DEBUGGER_BREAK_WATCHPOINT, "IDE_DEBUGGER_BREAK_WATCHPOINT", "watchpoint" },
diff --git a/libide/debugger/ide-debugger-types.h b/libide/debugger/ide-debugger-types.h
index a2a1353..96435c1 100644
--- a/libide/debugger/ide-debugger-types.h
+++ b/libide/debugger/ide-debugger-types.h
@@ -104,6 +104,7 @@ typedef enum
 
 /**
  * IdeDebuggerBreakMode:
+ * @IDE_DEBUGGER_BREAK_NONE: No breakpoint is set
  * @IDE_DEBUGGER_BREAK_BREAKPOINT: A simple breakpoint that stops the debugger
  *   when reaching a given location.
  * @IDE_DEBUGGER_BREAK_COUNTPOINT: A counter that is incremented when the
@@ -115,6 +116,7 @@ typedef enum
  */
 typedef enum
 {
+  IDE_DEBUGGER_BREAK_NONE = 0,
   IDE_DEBUGGER_BREAK_BREAKPOINT,
   IDE_DEBUGGER_BREAK_COUNTPOINT,
   IDE_DEBUGGER_BREAK_WATCHPOINT,
diff --git a/libide/debugger/ide-debugger-view.c b/libide/debugger/ide-debugger-view.c
new file mode 100644
index 0000000..69149cc
--- /dev/null
+++ b/libide/debugger/ide-debugger-view.c
@@ -0,0 +1,155 @@
+/* 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 <gtksourceview/gtksource.h>
+
+#include "ide-debug.h"
+
+#include "buffers/ide-buffer.h"
+#include "debugger/ide-debugger-gutter-renderer.h"
+#include "debugger/ide-debugger-view.h"
+
+struct _IdeDebuggerView
+{
+  IdeLayoutView              parent_instance;
+
+  GtkSourceView             *source_view;
+  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 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);
+
+  object_class->get_property = ide_debugger_view_get_property;
+  object_class->set_property = ide_debugger_view_set_property;
+
+  properties [PROP_BUFFER] =
+    g_param_spec_object ("buffer",
+                         "Buffer",
+                         "The buffer for the view",
+                         GTK_SOURCE_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))
+    {
+      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..5151c04
--- /dev/null
+++ b/libide/debugger/ide-debugger-view.h
@@ -0,0 +1,36 @@
+/* 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/>.
+ */
+
+#pragma once
+
+#include <gtksourceview/gtksource.h>
+
+#include "layout/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
diff --git a/libide/debugger/ide-debugger-view.ui b/libide/debugger/ide-debugger-view.ui
new file mode 100644
index 0000000..42c911e
--- /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="GtkSourceView" 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
new file mode 100644
index 0000000..e6f5161
--- /dev/null
+++ b/libide/debugger/ide-debugger-workbench-addin.c
@@ -0,0 +1,260 @@
+/* ide-debugger-workbench-addin.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-workbench-addin"
+
+#include <dazzle.h>
+#include <glib/gi18n.h>
+#include <gtksourceview/gtksource.h>
+
+#include "ide-context.h"
+#include "ide-debug.h"
+
+#include "debugger/ide-debug-manager.h"
+#include "debugger/ide-debugger-controls.h"
+#include "debugger/ide-debugger-perspective.h"
+#include "debugger/ide-debugger-workbench-addin.h"
+#include "runner/ide-run-manager.h"
+#include "workbench/ide-workbench-addin.h"
+#include "workbench/ide-workbench-message.h"
+
+struct _IdeDebuggerWorkbenchAddin
+{
+  GObject                 parent_instance;
+  IdeWorkbench           *workbench;
+  IdeDebuggerControls    *controls;
+  IdeWorkbenchMessage    *message;
+  IdeDebuggerPerspective *perspective;
+  DzlSignalGroup         *debug_manager_signals;
+};
+
+static void
+debugger_run_handler (IdeRunManager *run_manager,
+                      IdeRunner     *runner,
+                      gpointer       user_data)
+{
+  IdeDebuggerWorkbenchAddin *self = user_data;
+  IdeDebugManager *debug_manager;
+  IdeContext *context;
+  g_autoptr(GError) error = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_RUN_MANAGER (run_manager));
+  g_assert (IDE_IS_RUNNER (runner));
+  g_assert (IDE_IS_DEBUGGER_WORKBENCH_ADDIN (self));
+
+  ide_workbench_set_visible_perspective_name (self->workbench, "debugger");
+
+  /*
+   * Get the currently configured debugger and attach it to our runner.
+   * It might need to prepend arguments like `gdb', `pdb', `mdb', etc.
+   */
+  context = ide_object_get_context (IDE_OBJECT (run_manager));
+  debug_manager = ide_context_get_debug_manager (context);
+
+  if (!ide_debug_manager_start (debug_manager, runner, &error))
+    {
+      ide_workbench_message_set_subtitle (self->message, error->message);
+      gtk_widget_show (GTK_WIDGET (self->message));
+    }
+
+  IDE_EXIT;
+}
+
+static void
+debug_manager_notify_debugger (IdeDebuggerWorkbenchAddin *self,
+                               GParamSpec                *pspec,
+                               IdeDebugManager           *debug_manager)
+{
+  IdeDebugger *debugger;
+
+  g_assert (IDE_IS_DEBUGGER_WORKBENCH_ADDIN (self));
+  g_assert (IDE_IS_DEBUG_MANAGER (debug_manager));
+
+  debugger = ide_debug_manager_get_debugger (debug_manager);
+
+  gtk_widget_insert_action_group (GTK_WIDGET (self->workbench),
+                                  "debugger",
+                                  G_ACTION_GROUP (debugger));
+}
+
+static void
+debug_manager_notify_active (IdeDebuggerWorkbenchAddin *self,
+                             GParamSpec                *pspec,
+                             IdeDebugManager           *debug_manager)
+{
+  g_assert (IDE_IS_DEBUGGER_WORKBENCH_ADDIN (self));
+  g_assert (IDE_IS_DEBUG_MANAGER (debug_manager));
+
+  /*
+   * Instead of using a property binding, we use this signal callback so
+   * that we can adjust the reveal-child and visible. Otherwise the widgets
+   * will take up space+padding when reveal-child is FALSE.
+   */
+
+  if (ide_debug_manager_get_active (debug_manager))
+    {
+      gtk_widget_show (GTK_WIDGET (self->controls));
+      gtk_revealer_set_reveal_child (GTK_REVEALER (self->controls), TRUE);
+    }
+  else
+    {
+      gtk_revealer_set_reveal_child (GTK_REVEALER (self->controls), FALSE);
+    }
+}
+
+static void
+controls_notify_child_revealed (IdeDebuggerWorkbenchAddin *self,
+                                GParamSpec                *pspec,
+                                IdeDebuggerControls       *controls)
+{
+  g_assert (IDE_IS_DEBUGGER_WORKBENCH_ADDIN (self));
+  g_assert (IDE_IS_DEBUGGER_CONTROLS (controls));
+
+  if (!gtk_revealer_get_child_revealed (GTK_REVEALER (controls)))
+    gtk_widget_hide (GTK_WIDGET (controls));
+}
+
+static void
+ide_debugger_workbench_addin_load (IdeWorkbenchAddin *addin,
+                                   IdeWorkbench      *workbench)
+{
+  IdeDebuggerWorkbenchAddin *self = (IdeDebuggerWorkbenchAddin *)addin;
+  IdeWorkbenchHeaderBar *headerbar;
+  IdeDebugManager *debug_manager;
+  IdeRunManager *run_manager;
+  IdeContext *context;
+
+  g_assert (IDE_IS_WORKBENCH_ADDIN (addin));
+  g_assert (IDE_IS_WORKBENCH (workbench));
+
+  self->workbench = workbench;
+
+  context = ide_workbench_get_context (workbench);
+  debug_manager = ide_context_get_debug_manager (context);
+  run_manager = ide_context_get_run_manager (context);
+
+  headerbar = ide_workbench_get_headerbar (workbench);
+
+  self->debug_manager_signals = dzl_signal_group_new (IDE_TYPE_DEBUG_MANAGER);
+
+  dzl_signal_group_connect_swapped (self->debug_manager_signals,
+                                    "notify::active",
+                                    G_CALLBACK (debug_manager_notify_active),
+                                    self);
+
+  dzl_signal_group_connect_swapped (self->debug_manager_signals,
+                                    "notify::debugger",
+                                    G_CALLBACK (debug_manager_notify_debugger),
+                                    self);
+
+  dzl_signal_group_set_target (self->debug_manager_signals, debug_manager);
+
+  self->controls = g_object_new (IDE_TYPE_DEBUGGER_CONTROLS,
+                                 "transition-duration", 500,
+                                 "transition-type", GTK_REVEALER_TRANSITION_TYPE_SLIDE_RIGHT,
+                                 "reveal-child", FALSE,
+                                 "visible", FALSE,
+                                 NULL);
+  g_signal_connect_object (self->controls,
+                           "notify::child-revealed",
+                           G_CALLBACK (controls_notify_child_revealed),
+                           self,
+                           G_CONNECT_SWAPPED);
+  ide_workbench_header_bar_insert_left (headerbar,
+                                        GTK_WIDGET (self->controls),
+                                        GTK_PACK_START,
+                                        100);
+
+  ide_run_manager_add_handler (run_manager,
+                               "debugger",
+                               _("Run with Debugger"),
+                               "builder-debugger-symbolic",
+                               "F5",
+                               debugger_run_handler,
+                               g_object_ref (self),
+                               g_object_unref);
+
+  self->perspective = g_object_new (IDE_TYPE_DEBUGGER_PERSPECTIVE,
+                                    "visible", TRUE,
+                                    NULL);
+  g_object_bind_property (debug_manager, "debugger",
+                          self->perspective, "debugger",
+                          G_BINDING_SYNC_CREATE);
+  ide_workbench_add_perspective (workbench, IDE_PERSPECTIVE (self->perspective));
+
+  self->message = g_object_new (IDE_TYPE_WORKBENCH_MESSAGE,
+                                "id", "org.gnome.builder.debugger.failure",
+                                "show-close-button", TRUE,
+                                "title", _("Failed to initialize the debugger"),
+                                NULL);
+  ide_workbench_push_message (workbench, self->message);
+}
+
+static void
+ide_debugger_workbench_addin_unload (IdeWorkbenchAddin *addin,
+                                     IdeWorkbench      *workbench)
+{
+  IdeDebuggerWorkbenchAddin *self = (IdeDebuggerWorkbenchAddin *)addin;
+  IdeRunManager *run_manager;
+  IdeContext *context;
+
+  g_assert (IDE_IS_WORKBENCH_ADDIN (addin));
+  g_assert (IDE_IS_WORKBENCH (workbench));
+
+  context = ide_workbench_get_context (workbench);
+  run_manager = ide_context_get_run_manager (context);
+
+  g_clear_object (&self->debug_manager_signals);
+
+  /* Remove the handler to initiate the debugger */
+  ide_run_manager_remove_handler (run_manager, "debugger");
+
+  /* Remove our debugger control widgets */
+  g_clear_pointer (&self->controls, gtk_widget_destroy);
+
+  /* Remove actions from activation */
+  gtk_widget_insert_action_group (GTK_WIDGET (workbench), "debugger", NULL);
+
+  /* Remove the debugging perspective from the UI */
+  ide_workbench_remove_perspective (workbench, IDE_PERSPECTIVE (self->perspective));
+  self->perspective = NULL;
+
+  self->workbench = NULL;
+}
+
+static void
+workbench_addin_iface_init (IdeWorkbenchAddinInterface *iface)
+{
+  iface->load = ide_debugger_workbench_addin_load;
+  iface->unload = ide_debugger_workbench_addin_unload;
+}
+
+G_DEFINE_TYPE_WITH_CODE (IdeDebuggerWorkbenchAddin, ide_debugger_workbench_addin, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (IDE_TYPE_WORKBENCH_ADDIN, workbench_addin_iface_init))
+
+static void
+ide_debugger_workbench_addin_class_init (IdeDebuggerWorkbenchAddinClass *klass)
+{
+}
+
+static void
+ide_debugger_workbench_addin_init (IdeDebuggerWorkbenchAddin *self)
+{
+}
diff --git a/libide/debugger/ide-debugger-workbench-addin.h b/libide/debugger/ide-debugger-workbench-addin.h
new file mode 100644
index 0000000..df10b61
--- /dev/null
+++ b/libide/debugger/ide-debugger-workbench-addin.h
@@ -0,0 +1,29 @@
+/* ide-debugger-workbench-addin.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/>.
+ */
+
+#pragma once
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_DEBUGGER_WORKBENCH_ADDIN (ide_debugger_workbench_addin_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeDebuggerWorkbenchAddin, ide_debugger_workbench_addin, IDE, 
DEBUGGER_WORKBENCH_ADDIN, GObject)
+
+G_END_DECLS
diff --git a/libide/ide-context.h b/libide/ide-context.h
index 7c6c50f..55a06ae 100644
--- a/libide/ide-context.h
+++ b/libide/ide-context.h
@@ -36,6 +36,7 @@ IdeBufferManager         *ide_context_get_buffer_manager        (IdeContext
 IdeBuildManager          *ide_context_get_build_manager         (IdeContext           *self);
 IdeBuildSystem           *ide_context_get_build_system          (IdeContext           *self);
 IdeConfigurationManager  *ide_context_get_configuration_manager (IdeContext           *self);
+IdeDebugManager          *ide_context_get_debug_manager         (IdeContext           *self);
 IdeDiagnosticsManager    *ide_context_get_diagnostics_manager   (IdeContext           *self);
 IdeDeviceManager         *ide_context_get_device_manager        (IdeContext           *self);
 IdeDocumentation         *ide_context_get_documentation         (IdeContext           *self);
diff --git a/libide/ide-types.h b/libide/ide-types.h
index 9a02b4a..0661c15 100644
--- a/libide/ide-types.h
+++ b/libide/ide-types.h
@@ -47,6 +47,14 @@ typedef struct _IdeConfigurationManager        IdeConfigurationManager;
 
 typedef struct _IdeContext                     IdeContext;
 
+typedef struct _IdeDebugManager                IdeDebugManager;
+typedef struct _IdeDebugger                    IdeDebugger;
+typedef struct _IdeDebuggerBreakpoint          IdeDebuggerBreakpoint;
+typedef struct _IdeDebuggerFrame               IdeDebuggerFrame;
+typedef struct _IdeDebuggerInstruction         IdeDebuggerInstruction;
+typedef struct _IdeDebuggerRegister            IdeDebuggerRegister;
+typedef struct _IdeDebuggerVariable            IdeDebuggerVariable;
+
 typedef struct _IdeDevice                      IdeDevice;
 typedef struct _IdeDeviceManager               IdeDeviceManager;
 typedef struct _IdeDeviceProvider              IdeDeviceProvider;
diff --git a/libide/libide.gresource.xml b/libide/libide.gresource.xml
index f3d97a5..0a4a9b9 100644
--- a/libide/libide.gresource.xml
+++ b/libide/libide.gresource.xml
@@ -55,6 +55,14 @@
   </gresource>
 
   <gresource prefix="/org/gnome/builder/ui">
+    <file compressed="true" preprocess="xml-stripblanks" 
alias="ide-debugger-breakpoints-view.ui">debugger/ide-debugger-breakpoints-view.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks" 
alias="ide-debugger-controls.ui">debugger/ide-debugger-controls.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks" 
alias="ide-debugger-libraries-view.ui">debugger/ide-debugger-libraries-view.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks" 
alias="ide-debugger-locals-view.ui">debugger/ide-debugger-locals-view.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks" 
alias="ide-debugger-perspective.ui">debugger/ide-debugger-perspective.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks" 
alias="ide-debugger-registers-view.ui">debugger/ide-debugger-registers-view.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks" 
alias="ide-debugger-threads-view.ui">debugger/ide-debugger-threads-view.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks" 
alias="ide-debugger-view.ui">debugger/ide-debugger-view.ui</file>
     <file compressed="true" preprocess="xml-stripblanks" 
alias="ide-layout-pane.ui">layout/ide-layout-pane.ui</file>
     <file compressed="true" preprocess="xml-stripblanks" 
alias="ide-layout-stack-header.ui">layout/ide-layout-stack-header.ui</file>
     <file compressed="true" preprocess="xml-stripblanks" 
alias="ide-layout-stack.ui">layout/ide-layout-stack.ui</file>
@@ -83,6 +91,10 @@
     <file alias="buildconfig.plugin">buildconfig/buildconfig.plugin</file>
   </gresource>
 
+  <gresource prefix="/org/gnome/builder/plugins/debugger">
+    <file alias="debugger.plugin">debugger/debugger.plugin</file>
+  </gresource>
+
   <gresource prefix="/org/gnome/builder/plugins/editor">
     <file alias="editor.plugin">editor/editor.plugin</file>
   </gresource>
diff --git a/libide/meson.build b/libide/meson.build
index b03c7e8..074ae56 100644
--- a/libide/meson.build
+++ b/libide/meson.build
@@ -90,6 +90,7 @@ libide_public_headers = [
   'debugger/ide-debugger-frame.h',
   'debugger/ide-debugger-instruction.h',
   'debugger/ide-debugger-library.h',
+  'debugger/ide-debugger-perspective.h',
   'debugger/ide-debugger-register.h',
   'debugger/ide-debugger-thread-group.h',
   'debugger/ide-debugger-thread.h',
@@ -303,6 +304,7 @@ libide_public_sources = [
   'debugger/ide-debugger-frame.c',
   'debugger/ide-debugger-instruction.c',
   'debugger/ide-debugger-library.c',
+  'debugger/ide-debugger-perspective.c',
   'debugger/ide-debugger-register.c',
   'debugger/ide-debugger-thread-group.c',
   'debugger/ide-debugger-thread.c',
@@ -509,16 +511,27 @@ libide_sources = libide_generated_headers + libide_public_sources + [
   'debugger/ide-debugger-address-map.h',
   'debugger/ide-debugger-breakpoints-view.c',
   'debugger/ide-debugger-breakpoints-view.h',
+  'debugger/ide-debugger-controls.c',
+  'debugger/ide-debugger-controls.h',
   'debugger/ide-debugger-fallbacks.c',
+  'debugger/ide-debugger-breakpoints.c',
+  'debugger/ide-debugger-breakpoints.h',
+  'debugger/ide-debugger-gutter-renderer.c',
+  'debugger/ide-debugger-gutter-renderer.h',
   'debugger/ide-debugger-libraries-view.c',
   'debugger/ide-debugger-libraries-view.h',
   'debugger/ide-debugger-locals-view.c',
   'debugger/ide-debugger-locals-view.h',
+  'debugger/ide-debugger-plugin.c',
   'debugger/ide-debugger-private.h',
   'debugger/ide-debugger-registers-view.c',
   'debugger/ide-debugger-registers-view.h',
   'debugger/ide-debugger-threads-view.c',
   'debugger/ide-debugger-threads-view.h',
+  'debugger/ide-debugger-view.c',
+  'debugger/ide-debugger-view.h',
+  'debugger/ide-debugger-workbench-addin.c',
+  'debugger/ide-debugger-workbench-addin.h',
   'editor/ide-editor-layout-stack-addin.c',
   'editor/ide-editor-layout-stack-addin.h',
   'editor/ide-editor-layout-stack-controls.c',


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