[gnome-builder/wip/chergert/debugger: 9/100] debugger: stub out various debugger API mechanics



commit 3ca53529f57c68414a6ce114ca578d1c4e7b78e0
Author: Christian Hergert <chergert redhat com>
Date:   Tue Mar 21 00:52:54 2017 -0700

    debugger: stub out various debugger API mechanics

 data/icons/hicolor/icons.gresource.xml             |    1 +
 .../scalable/actions/builder-debugger-symbolic.svg |   27 ++
 libide/Makefile.am                                 |   15 +
 libide/debugger/debugger.plugin                    |   10 +
 libide/debugger/ide-debug-manager.c                |  178 ++++++++++++
 libide/debugger/ide-debug-manager.h                |   36 +++
 libide/debugger/ide-debugger-breakpoints.c         |  292 ++++++++++++++++++++
 libide/debugger/ide-debugger-breakpoints.h         |   49 ++++
 libide/debugger/ide-debugger-controls.c            |   40 +++
 libide/debugger/ide-debugger-controls.h            |   38 +++
 libide/debugger/ide-debugger-controls.ui           |   92 ++++++
 libide/debugger/ide-debugger-editor-view-addin.c   |  161 +++++++++++
 libide/debugger/ide-debugger-editor-view-addin.h   |   32 +++
 libide/debugger/ide-debugger-gutter-renderer.c     |  280 +++++++++++++++++++
 libide/debugger/ide-debugger-gutter-renderer.h     |   35 +++
 libide/debugger/ide-debugger-perspective.c         |  126 +++++++++
 libide/debugger/ide-debugger-perspective.h         |   32 +++
 libide/debugger/ide-debugger-perspective.ui        |    5 +
 libide/debugger/ide-debugger-plugin.c              |   35 +++
 libide/debugger/ide-debugger-workbench-addin.c     |  175 ++++++++++++
 libide/debugger/ide-debugger-workbench-addin.h     |   32 +++
 libide/ide-context.c                               |   22 ++
 libide/ide-context.h                               |    1 +
 libide/ide-types.h                                 |    3 +
 libide/resources/libide.gresource.xml              |   10 +-
 25 files changed, 1725 insertions(+), 2 deletions(-)
---
diff --git a/data/icons/hicolor/icons.gresource.xml b/data/icons/hicolor/icons.gresource.xml
index d078183..1ced982 100644
--- a/data/icons/hicolor/icons.gresource.xml
+++ b/data/icons/hicolor/icons.gresource.xml
@@ -3,6 +3,7 @@
   <gresource prefix="/org/gnome/builder/icons">
     <file compressed="true">scalable/actions/builder-build-configure-symbolic.svg</file>
     <file compressed="true">scalable/actions/builder-build-symbolic.svg</file>
+    <file compressed="true">scalable/actions/builder-debugger-symbolic.svg</file>
     <file compressed="true">scalable/actions/builder-move-left-symbolic.svg</file>
     <file compressed="true">scalable/actions/builder-move-right-symbolic.svg</file>
     <file compressed="true">scalable/actions/builder-split-tab-left-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/Makefile.am b/libide/Makefile.am
index 76e3bac..66ffe05 100644
--- a/libide/Makefile.am
+++ b/libide/Makefile.am
@@ -49,6 +49,12 @@ libide_1_0_la_public_headers =                                              \
        buildsystem/ide-configuration-provider.h                            \
        buildsystem/ide-environment-variable.h                              \
        buildsystem/ide-environment.h                                       \
+       debugger/ide-debug-manager.h                                        \
+       debugger/ide-debugger-breakpoints.h                                 \
+       debugger/ide-debugger-editor-view-addin.h                           \
+       debugger/ide-debugger-perspective.h                                 \
+       debugger/ide-debugger-workbench-addin.h                             \
+       debugger/ide-debugger-gutter-renderer.h                             \
        devices/ide-device-manager.h                                        \
        devices/ide-device-provider.h                                       \
        devices/ide-device.h                                                \
@@ -230,6 +236,12 @@ libide_1_0_la_public_sources =                                              \
        buildsystem/ide-configuration-provider.c                            \
        buildsystem/ide-environment-variable.c                              \
        buildsystem/ide-environment.c                                       \
+       debugger/ide-debug-manager.c                                        \
+       debugger/ide-debugger-breakpoints.c                                 \
+       debugger/ide-debugger-editor-view-addin.c                           \
+       debugger/ide-debugger-perspective.c                                 \
+       debugger/ide-debugger-workbench-addin.c                             \
+       debugger/ide-debugger-gutter-renderer.c                             \
        devices/ide-device-manager.c                                        \
        devices/ide-device-provider.c                                       \
        devices/ide-device.c                                                \
@@ -422,6 +434,9 @@ libide_1_0_la_SOURCES =                                                     \
        buildui/ide-environment-editor-row.h                                \
        buildui/ide-environment-editor.c                                    \
        buildui/ide-environment-editor.h                                    \
+       debugger/ide-debugger-controls.c                                    \
+       debugger/ide-debugger-controls.h                                    \
+       debugger/ide-debugger-plugin.c                                      \
        editor/ide-editor-frame-actions.c                                   \
        editor/ide-editor-frame-actions.h                                   \
        editor/ide-editor-frame-private.h                                   \
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
new file mode 100644
index 0000000..401bbcb
--- /dev/null
+++ b/libide/debugger/ide-debug-manager.c
@@ -0,0 +1,178 @@
+/* ide-debug-manager.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-debug-manager"
+
+#include <glib/gi18n.h>
+
+#include "ide-debug-manager.h"
+
+struct _IdeDebugManager
+{
+  IdeObject           parent_instance;
+  GSimpleActionGroup *actions;
+};
+
+enum {
+  PROP_0,
+  N_PROPS
+};
+
+enum {
+  BREAKPOINT_ADDED,
+  BREAKPOING_REMOVED,
+  N_SIGNALS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static gboolean
+ide_debug_manager_has_action (GActionGroup *group,
+                              const gchar  *action_name)
+{
+  IdeDebugManager *self = (IdeDebugManager *)group;
+
+  g_assert (IDE_IS_DEBUG_MANAGER (self));
+
+  return g_action_group_has_action (G_ACTION_GROUP (self->actions), action_name);
+}
+
+static gchar **
+ide_debug_manager_list_actions (GActionGroup *group)
+{
+  IdeDebugManager *self = (IdeDebugManager *)group;
+
+  g_assert (IDE_IS_DEBUG_MANAGER (self));
+
+  return g_action_group_list_actions (G_ACTION_GROUP (self->actions));
+}
+
+static gboolean
+ide_debug_manager_query_action (GActionGroup        *group,
+                                const gchar         *action_name,
+                                gboolean            *enabled,
+                                const GVariantType **parameter_type,
+                                const GVariantType **state_type,
+                                GVariant           **state_hint,
+                                GVariant           **state)
+{
+  IdeDebugManager *self = (IdeDebugManager *)group;
+
+  g_assert (IDE_IS_DEBUG_MANAGER (self));
+
+  return g_action_group_query_action (G_ACTION_GROUP (self->actions),
+                                      action_name,
+                                      enabled,
+                                      parameter_type,
+                                      state_type,
+                                      state_hint,
+                                      state);
+}
+
+static void
+ide_debug_manager_activate_action (GActionGroup *group,
+                                   const gchar  *action_name,
+                                   GVariant     *parameter)
+{
+  IdeDebugManager *self = (IdeDebugManager *)group;
+
+  g_assert (IDE_IS_DEBUG_MANAGER (self));
+
+  g_action_group_activate_action (G_ACTION_GROUP (self->actions), action_name, parameter);
+}
+
+static void
+action_group_iface_init (GActionGroupInterface *iface)
+{
+  iface->has_action = ide_debug_manager_has_action;
+  iface->list_actions = ide_debug_manager_list_actions;
+  iface->query_action = ide_debug_manager_query_action;
+  iface->activate_action = ide_debug_manager_activate_action;
+}
+
+G_DEFINE_TYPE_EXTENDED (IdeDebugManager, ide_debug_manager, IDE_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, action_group_iface_init))
+
+static void
+ide_debug_manager_finalize (GObject *object)
+{
+  IdeDebugManager *self = (IdeDebugManager *)object;
+
+  g_clear_object (&self->actions);
+
+  G_OBJECT_CLASS (ide_debug_manager_parent_class)->finalize (object);
+}
+
+static void
+ide_debug_manager_get_property (GObject    *object,
+                                guint       prop_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
+{
+  IdeDebugManager *self = IDE_DEBUG_MANAGER (object);
+
+  switch (prop_id)
+    {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_debug_manager_set_property (GObject      *object,
+                                guint         prop_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+  IdeDebugManager *self = IDE_DEBUG_MANAGER (object);
+
+  switch (prop_id)
+    {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_debug_manager_class_init (IdeDebugManagerClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_debug_manager_finalize;
+  object_class->get_property = ide_debug_manager_get_property;
+  object_class->set_property = ide_debug_manager_set_property;
+}
+
+static void
+ide_debug_manager_init (IdeDebugManager *self)
+{
+  self->actions = g_simple_action_group_new ();
+}
+
+gboolean
+ide_debug_manager_start (IdeDebugManager  *self,
+                         IdeRunner        *runner,
+                         GError          **error)
+{
+  g_set_error (error,
+               G_IO_ERROR,
+               G_IO_ERROR_NOT_SUPPORTED,
+               _("A suitable debugger could not be found."));
+
+  return FALSE;
+}
diff --git a/libide/debugger/ide-debug-manager.h b/libide/debugger/ide-debug-manager.h
new file mode 100644
index 0000000..9453ff6
--- /dev/null
+++ b/libide/debugger/ide-debug-manager.h
@@ -0,0 +1,36 @@
+/* ide-debug-manager.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_DEBUG_MANAGER_H
+#define IDE_DEBUG_MANAGER_H
+
+#include "ide-object.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_DEBUG_MANAGER (ide_debug_manager_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeDebugManager, ide_debug_manager, IDE, DEBUG_MANAGER, IdeObject)
+
+gboolean ide_debug_manager_start (IdeDebugManager  *self,
+                                  IdeRunner        *runner,
+                                  GError          **error);
+
+G_END_DECLS
+
+#endif /* IDE_DEBUG_MANAGER_H */
diff --git a/libide/debugger/ide-debugger-breakpoints.c b/libide/debugger/ide-debugger-breakpoints.c
new file mode 100644
index 0000000..6e862e4
--- /dev/null
+++ b/libide/debugger/ide-debugger-breakpoints.c
@@ -0,0 +1,292 @@
+/* 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 "ide-debugger-breakpoints.h"
+
+/**
+ * SECTION:ide-debugger-breakpoints
+ * @title: IdeDebuggerBreakpoints
+ *
+ * This object is used to track breakpoints within a certain file.
+ * This is necessary so that we can draw the gutter for breakpoints
+ * very fast by reducing our breakpoint lookup cost to simply a line
+ * number in a hashtable.
+ *
+ * To do this, the #IdeDebuggerBreakpoints object will track changes
+ * to breakpoints (and watchpoints) in the IdeDebugManager. If the
+ * breakpoint is part to the current #IdeDebuggerBreakpoints:file, then
+ * it will cache the value. The #IdeDebuggerGutterRenderer is assigned
+ * a #IdeDebuggerBreakpoints which it will use to determine what icon
+ * to render on the gutter.
+ */
+
+struct _IdeDebuggerBreakpoints
+{
+  IdeObject   parent_instance;
+  GHashTable *cache;
+  GFile      *file;
+};
+
+enum {
+  PROP_0,
+  PROP_FILE,
+  N_PROPS
+};
+
+enum {
+  CHANGED,
+  N_SIGNALS
+};
+
+G_DEFINE_TYPE (IdeDebuggerBreakpoints, ide_debugger_breakpoints, IDE_TYPE_OBJECT)
+
+static GParamSpec *properties [N_PROPS];
+static guint signals [N_SIGNALS];
+
+static void
+on_breakpoint_added (IdeDebuggerBreakpoints *self,
+                     GFile                  *file,
+                     guint                   line,
+                     IdeDebuggerBreakType    break_type,
+                     IdeDebugManager        *debug_manager)
+{
+  IdeDebuggerBreakType type;
+  gpointer key = GUINT_TO_POINTER (line);
+
+  g_assert (IDE_IS_DEBUGGER_BREAKPOINTS (self));
+  g_assert (G_IS_FILE (file));
+
+  if (!g_file_equal (file, self->file))
+    return;
+
+  type = GPOINTER_TO_UINT (g_hash_table_lookup (self->cache, key)) | break_type;
+  g_hash_table_insert (self->cache, key, GUINT_TO_POINTER (type));
+
+  g_signal_emit (self, signals [CHANGED], 0);
+}
+
+static void
+on_breakpoint_removed (IdeDebuggerBreakpoints *self,
+                       GFile                  *file,
+                       guint                   line,
+                       IdeDebuggerBreakType    break_type,
+                       IdeDebugManager        *debug_manager)
+{
+  IdeDebuggerBreakType type;
+  gpointer key = GUINT_TO_POINTER (line);
+
+  g_assert (IDE_IS_DEBUGGER_BREAKPOINTS (self));
+  g_assert (G_IS_FILE (file));
+
+  if (!g_file_equal (file, self->file))
+    return;
+
+  type = GPOINTER_TO_UINT (g_hash_table_lookup (self->cache, key));
+  type = type & ~break_type;
+
+  if (type == 0)
+    g_hash_table_remove (self->cache, key);
+  else
+    g_hash_table_replace (self->cache, key, GUINT_TO_POINTER (type));
+
+  g_signal_emit (self, signals [CHANGED], 0);
+}
+
+static void
+ide_debugger_breakpoints_constructed (GObject *object)
+{
+  IdeDebuggerBreakpoints *self = (IdeDebuggerBreakpoints *)object;
+  IdeDebugManager *debug_manager;
+  IdeContext *context;
+
+  g_assert (IDE_IS_DEBUGGER_BREAKPOINTS (self));
+
+  G_OBJECT_CLASS (ide_debugger_breakpoints_parent_class)->constructed (object);
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  debug_manager = ide_context_get_debug_manager (context);
+
+  g_signal_connect_object (debug_manager,
+                           "breakpoint-added",
+                           G_CALLBACK (on_breakpoint_added),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  g_signal_connect_object (debug_manager,
+                           "breakpoint-removed",
+                           G_CALLBACK (on_breakpoint_removed),
+                           self,
+                           G_CONNECT_SWAPPED);
+}
+
+static void
+ide_debugger_breakpoints_finalize (GObject *object)
+{
+  IdeDebuggerBreakpoints *self = (IdeDebuggerBreakpoints *)object;
+
+  g_clear_object (&self->file);
+  g_clear_pointer (&self->cache, g_hash_table_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->constructed = ide_debugger_breakpoints_constructed;
+  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",
+                         "File",
+                         G_TYPE_FILE,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+
+  /**
+   * IdeDebuggerBreakpoints::changed:
+   *
+   * This signal is emitted when a breakpoint has changed with in the file
+   * indicated by IdeDebuggerBreakpoints:file.
+   */
+  signals [CHANGED] =
+    g_signal_new ("changed",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL, NULL, G_TYPE_NONE, 0);
+}
+
+static void
+ide_debugger_breakpoints_init (IdeDebuggerBreakpoints *self)
+{
+  self->cache = g_hash_table_new (NULL, NULL);
+}
+
+/**
+ * ide_debugger_breakpoints_get_file:
+ *
+ * Returns: (transfer none): A #GFile.
+ */
+GFile *
+ide_debugger_breakpoints_get_file (IdeDebuggerBreakpoints *self)
+{
+  g_return_val_if_fail (IDE_IS_DEBUGGER_BREAKPOINTS (self), NULL);
+
+  return self->file;
+}
+
+void
+ide_debugger_breakpoints_add (IdeDebuggerBreakpoints *self,
+                              guint                   line,
+                              IdeDebuggerBreakType    break_type)
+{
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_DEBUGGER_BREAKPOINTS (self));
+  g_assert (line > 0);
+  g_assert (break_type > IDE_DEBUGGER_BREAK_NONE);
+
+  /*
+   * TODO: Propagate request for breakpoint via debugger debug_manager.
+   *       Instead, for testing, we will just add the breakpoint
+   *       locally to our cache. However, a real implementation
+   *       should possibly set a pending bit on the entry and the
+   *       remove the pending bit once it has been added by the
+   *       debugger (since that could involve a round trip over
+   *       IPC and we don't want to show a full breakpoint before
+   *       its actually available).
+   */
+
+  g_hash_table_insert (self->cache, GUINT_TO_POINTER (line), GUINT_TO_POINTER (break_type));
+  g_signal_emit (self, signals [CHANGED], 0);
+
+  IDE_EXIT;
+}
+
+void
+ide_debugger_breakpoints_remove (IdeDebuggerBreakpoints *self,
+                                 guint                   line)
+{
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_DEBUGGER_BREAKPOINTS (self));
+  g_assert (line > 0);
+
+  /*
+   * TODO: Propagate request to remove breakpoint via debugger debug_manager.
+   *       For testing, we can just remove the debugger from cache.
+   */
+
+  g_hash_table_remove (self->cache, GUINT_TO_POINTER (line));
+  g_signal_emit (self, signals [CHANGED], 0);
+
+  IDE_EXIT;
+}
+
+IdeDebuggerBreakType
+ide_debugger_breakpoints_lookup (IdeDebuggerBreakpoints *self,
+                                 guint                   line)
+{
+  return GPOINTER_TO_UINT (g_hash_table_lookup (self->cache, GUINT_TO_POINTER (line)));
+}
diff --git a/libide/debugger/ide-debugger-breakpoints.h b/libide/debugger/ide-debugger-breakpoints.h
new file mode 100644
index 0000000..c87842e
--- /dev/null
+++ b/libide/debugger/ide-debugger-breakpoints.h
@@ -0,0 +1,49 @@
+/* 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/>.
+ */
+
+#ifndef IDE_DEBUGGER_BREAKPOINTS_H
+#define IDE_DEBUGGER_BREAKPOINTS_H
+
+#include <ide.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, IdeObject)
+
+typedef enum
+{
+  IDE_DEBUGGER_BREAK_NONE       = 0,
+  IDE_DEBUGGER_BREAK_BREAKPOINT = 1 << 0,
+  IDE_DEBUGGER_BREAK_COUNTPOINT = 1 << 1,
+  IDE_DEBUGGER_BREAK_WATCHPOINT = 1 << 2,
+} IdeDebuggerBreakType;
+
+GFile                *ide_debugger_breakpoints_get_file (IdeDebuggerBreakpoints *self);
+void                  ide_debugger_breakpoints_add      (IdeDebuggerBreakpoints *self,
+                                                         guint                   line,
+                                                         IdeDebuggerBreakType    break_type);
+void                  ide_debugger_breakpoints_remove   (IdeDebuggerBreakpoints *self,
+                                                         guint                   line);
+IdeDebuggerBreakType  ide_debugger_breakpoints_lookup   (IdeDebuggerBreakpoints *self,
+                                                         guint                   line);
+
+G_END_DECLS
+
+#endif /* IDE_DEBUGGER_BREAKPOINTS_H */
diff --git a/libide/debugger/ide-debugger-controls.c b/libide/debugger/ide-debugger-controls.c
new file mode 100644
index 0000000..a2be212
--- /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_BIN)
+
+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..0b4d4c1
--- /dev/null
+++ b/libide/debugger/ide-debugger-controls.h
@@ -0,0 +1,38 @@
+/* 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"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_DEBUGGER_CONTROLS (ide_debugger_controls_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeDebuggerControls, ide_debugger_controls, IDE, DEBUGGER_CONTROLS, GtkBin)
+
+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..558168e
--- /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="GtkBin">
+    <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">debugger.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">debugger.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">debugger.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">debugger.step-in</property>
+            <property name="tooltip-text" translatable="yes">Step into the scope</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_out_button">
+            <property name="action-name">debugger.step-out</property>
+            <property name="tooltip-text" translatable="yes">Step out of scope</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>
+        <child>
+          <object class="GtkButton" id="step_over_button">
+            <property name="action-name">debugger.step-over</property>
+            <property name="tooltip-text" translatable="yes">Step over the operation</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>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/libide/debugger/ide-debugger-editor-view-addin.c 
b/libide/debugger/ide-debugger-editor-view-addin.c
new file mode 100644
index 0000000..2b7a3b9
--- /dev/null
+++ b/libide/debugger/ide-debugger-editor-view-addin.c
@@ -0,0 +1,161 @@
+/* ide-debugger-editor-view-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-editor-view-addin"
+
+#include "ide-debugger-breakpoints.h"
+#include "ide-debugger-editor-view-addin.h"
+#include "ide-debugger-gutter-renderer.h"
+
+static GHashTable *supported_languages;
+
+struct _IdeDebuggerEditorViewAddin
+{
+  GObject    parent_instance;
+  GPtrArray *renderers;
+};
+
+static void
+ide_debugger_editor_view_addin_load_source_view (IdeEditorViewAddin *addin,
+                                                 IdeSourceView      *source_view)
+{
+  IdeDebuggerEditorViewAddin *self = (IdeDebuggerEditorViewAddin *)addin;
+  g_autoptr(IdeDebuggerBreakpoints) breakpoints = NULL;
+  GtkSourceGutterRenderer *renderer;
+  GtkSourceGutter *gutter;
+  IdeContext *context;
+  IdeBuffer *buffer;
+  IdeFile *file;
+  GFile *gfile;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_DEBUGGER_EDITOR_VIEW_ADDIN (addin));
+  g_assert (IDE_IS_SOURCE_VIEW (source_view));
+
+  context = ide_widget_get_context (GTK_WIDGET (source_view));
+  buffer = IDE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (source_view)));
+  file = ide_buffer_get_file (buffer);
+  gfile = ide_file_get_file (file);
+
+  breakpoints = g_object_new (IDE_TYPE_DEBUGGER_BREAKPOINTS,
+                              "context", context,
+                              "file", gfile,
+                              NULL);
+
+  gutter = gtk_source_view_get_gutter (GTK_SOURCE_VIEW (source_view), GTK_TEXT_WINDOW_LEFT);
+  renderer = g_object_new (IDE_TYPE_DEBUGGER_GUTTER_RENDERER,
+                           "alignment-mode", GTK_SOURCE_GUTTER_RENDERER_ALIGNMENT_MODE_FIRST,
+                           "breakpoints", breakpoints,
+                           "size", 16,
+                           "visible", TRUE,
+                           "xalign", 0.5f,
+                           NULL);
+  g_ptr_array_add (self->renderers, g_object_ref (renderer));
+  gtk_source_gutter_insert (gutter, renderer, -1000);
+
+  IDE_EXIT;
+}
+
+static void
+ide_debugger_editor_view_addin_unload_source_view (IdeEditorViewAddin *addin,
+                                                   IdeSourceView      *source_view)
+{
+  IdeDebuggerEditorViewAddin *self = (IdeDebuggerEditorViewAddin *)addin;
+  GtkSourceGutter *gutter;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_DEBUGGER_EDITOR_VIEW_ADDIN (addin));
+  g_assert (IDE_IS_SOURCE_VIEW (source_view));
+
+  gutter = gtk_source_view_get_gutter (GTK_SOURCE_VIEW (source_view), GTK_TEXT_WINDOW_LEFT);
+
+  for (guint i = self->renderers->len; i > 0; i--)
+    {
+      GtkSourceGutterRenderer *renderer = g_ptr_array_index (self->renderers, i - 1);
+
+      if ((gpointer)source_view == (gpointer)gtk_source_gutter_renderer_get_view (renderer))
+        {
+          gtk_source_gutter_remove (gutter, renderer);
+          g_ptr_array_remove_index (self->renderers, i - 1);
+        }
+    }
+
+  IDE_EXIT;
+}
+
+static void
+ide_debugger_editor_view_addin_language_changed (IdeEditorViewAddin *addin,
+                                                 const gchar        *lang_id)
+{
+  IdeDebuggerEditorViewAddin *self = (IdeDebuggerEditorViewAddin *)addin;
+  gboolean visible = FALSE;
+
+  g_assert (IDE_IS_DEBUGGER_EDITOR_VIEW_ADDIN (self));
+
+  visible = lang_id && g_hash_table_contains (supported_languages, lang_id);
+
+  for (guint i = 0; i < self->renderers->len; i++)
+    {
+      GtkSourceGutterRenderer *renderer = g_ptr_array_index (self->renderers, i);
+
+      gtk_source_gutter_renderer_set_visible (renderer, visible);
+    }
+}
+
+static void
+editor_view_addin_iface_init (IdeEditorViewAddinInterface *iface)
+{
+  iface->load_source_view = ide_debugger_editor_view_addin_load_source_view;
+  iface->unload_source_view = ide_debugger_editor_view_addin_unload_source_view;
+  iface->language_changed = ide_debugger_editor_view_addin_language_changed;
+}
+
+G_DEFINE_TYPE_EXTENDED (IdeDebuggerEditorViewAddin, ide_debugger_editor_view_addin, G_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (IDE_TYPE_EDITOR_VIEW_ADDIN, editor_view_addin_iface_init))
+
+static void
+ide_debugger_editor_view_addin_finalize (GObject *object)
+{
+  IdeDebuggerEditorViewAddin *self = (IdeDebuggerEditorViewAddin *)object;
+
+  g_clear_pointer (&self->renderers, g_ptr_array_unref);
+
+  G_OBJECT_CLASS (ide_debugger_editor_view_addin_parent_class)->finalize (object);
+}
+
+static void
+ide_debugger_editor_view_addin_class_init (IdeDebuggerEditorViewAddinClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_debugger_editor_view_addin_finalize;
+
+  supported_languages = g_hash_table_new (g_str_hash, g_str_equal);
+  g_hash_table_insert (supported_languages, (gchar *)"c", NULL);
+  g_hash_table_insert (supported_languages, (gchar *)"chdr", NULL);
+  g_hash_table_insert (supported_languages, (gchar *)"cpp", NULL);
+  g_hash_table_insert (supported_languages, (gchar *)"cpphdr", NULL);
+}
+
+static void
+ide_debugger_editor_view_addin_init (IdeDebuggerEditorViewAddin *self)
+{
+  self->renderers = g_ptr_array_new_with_free_func (g_object_unref);
+}
diff --git a/libide/debugger/ide-debugger-editor-view-addin.h 
b/libide/debugger/ide-debugger-editor-view-addin.h
new file mode 100644
index 0000000..58cc06e
--- /dev/null
+++ b/libide/debugger/ide-debugger-editor-view-addin.h
@@ -0,0 +1,32 @@
+/* ide-debugger-editor-view-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/>.
+ */
+
+#ifndef IDE_DEBUGGER_EDITOR_VIEW_ADDIN_H
+#define IDE_DEBUGGER_EDITOR_VIEW_ADDIN_H
+
+#include <ide.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_DEBUGGER_EDITOR_VIEW_ADDIN (ide_debugger_editor_view_addin_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeDebuggerEditorViewAddin, ide_debugger_editor_view_addin, IDE, 
DEBUGGER_EDITOR_VIEW_ADDIN, GObject)
+
+G_END_DECLS
+
+#endif /* IDE_DEBUGGER_EDITOR_VIEW_ADDIN_H */
diff --git a/libide/debugger/ide-debugger-gutter-renderer.c b/libide/debugger/ide-debugger-gutter-renderer.c
new file mode 100644
index 0000000..6a1bb3d
--- /dev/null
+++ b/libide/debugger/ide-debugger-gutter-renderer.c
@@ -0,0 +1,280 @@
+/* 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-debugger-gutter-renderer.h"
+#include "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)
+{
+  IdeDebuggerBreakType 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_lookup (self->breakpoints, line);
+
+  switch (break_type)
+    {
+    case IDE_DEBUGGER_BREAK_NONE:
+      ide_debugger_breakpoints_add (self->breakpoints, line, IDE_DEBUGGER_BREAK_BREAKPOINT);
+      break;
+
+    case IDE_DEBUGGER_BREAK_BREAKPOINT:
+    case IDE_DEBUGGER_BREAK_COUNTPOINT:
+    case IDE_DEBUGGER_BREAK_WATCHPOINT:
+      ide_debugger_breakpoints_remove (self->breakpoints, line);
+      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)
+{
+  IdeDebuggerBreakType 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_lookup (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;
+    }
+}
+
+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));
+}
+
+static 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..e3780ff
--- /dev/null
+++ b/libide/debugger/ide-debugger-gutter-renderer.h
@@ -0,0 +1,35 @@
+/* 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/>.
+ */
+
+#ifndef IDE_DEBUGGER_GUTTER_RENDERER_H
+#define IDE_DEBUGGER_GUTTER_RENDERER_H
+
+#include <gtksourceview/gtksource.h>
+#include <ide.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);
+
+G_END_DECLS
+
+#endif /* IDE_DEBUGGER_GUTTER_RENDERER_H */
diff --git a/libide/debugger/ide-debugger-perspective.c b/libide/debugger/ide-debugger-perspective.c
new file mode 100644
index 0000000..a2dd8e5
--- /dev/null
+++ b/libide/debugger/ide-debugger-perspective.c
@@ -0,0 +1,126 @@
+/* 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 <glib/gi18n.h>
+
+#include "ide-debugger-perspective.h"
+
+struct _IdeDebuggerPerspective
+{
+  IdeLayout parent_instance;
+};
+
+enum {
+  PROP_0,
+  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_EXTENDED (IdeDebuggerPerspective, ide_debugger_perspective, IDE_TYPE_LAYOUT, 0,
+                        G_IMPLEMENT_INTERFACE (IDE_TYPE_PERSPECTIVE, perspective_iface_init))
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+ide_debugger_perspective_finalize (GObject *object)
+{
+  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)
+    {
+    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)
+    {
+    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;
+
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/builder/ui/ide-debugger-perspective.ui");
+}
+
+static void
+ide_debugger_perspective_init (IdeDebuggerPerspective *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+}
diff --git a/libide/debugger/ide-debugger-perspective.h b/libide/debugger/ide-debugger-perspective.h
new file mode 100644
index 0000000..18ee206
--- /dev/null
+++ b/libide/debugger/ide-debugger-perspective.h
@@ -0,0 +1,32 @@
+/* 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/>.
+ */
+
+#ifndef IDE_DEBUGGER_PERSPECTIVE_H
+#define IDE_DEBUGGER_PERSPECTIVE_H
+
+#include <ide.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)
+
+G_END_DECLS
+
+#endif /* IDE_DEBUGGER_PERSPECTIVE_H */
diff --git a/libide/debugger/ide-debugger-perspective.ui b/libide/debugger/ide-debugger-perspective.ui
new file mode 100644
index 0000000..6e5eedb
--- /dev/null
+++ b/libide/debugger/ide-debugger-perspective.ui
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="IdeDebuggerPerspective" parent="IdeLayout">
+  </template>
+</interface>
diff --git a/libide/debugger/ide-debugger-plugin.c b/libide/debugger/ide-debugger-plugin.c
new file mode 100644
index 0000000..846b772
--- /dev/null
+++ b/libide/debugger/ide-debugger-plugin.c
@@ -0,0 +1,35 @@
+/* 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>
+
+#include "debugger/ide-debugger-editor-view-addin.h"
+#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)
+{
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_EDITOR_VIEW_ADDIN,
+                                              IDE_TYPE_DEBUGGER_EDITOR_VIEW_ADDIN);
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_WORKBENCH_ADDIN,
+                                              IDE_TYPE_DEBUGGER_WORKBENCH_ADDIN);
+}
diff --git a/libide/debugger/ide-debugger-workbench-addin.c b/libide/debugger/ide-debugger-workbench-addin.c
new file mode 100644
index 0000000..25cb184
--- /dev/null
+++ b/libide/debugger/ide-debugger-workbench-addin.c
@@ -0,0 +1,175 @@
+/* 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 <glib/gi18n.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 "workbench/ide-workbench-message.h"
+
+struct _IdeDebuggerWorkbenchAddin
+{
+  GObject                 parent_instance;
+  IdeWorkbench           *workbench;
+  IdeDebuggerControls    *controls;
+  IdeWorkbenchMessage    *message;
+  IdeDebuggerPerspective *perspective;
+};
+
+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
+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);
+
+  gtk_widget_insert_action_group (GTK_WIDGET (workbench),
+                                  "debugger",
+                                  G_ACTION_GROUP (debug_manager));
+
+  headerbar = ide_workbench_get_headerbar (workbench);
+
+  self->controls = g_object_new (IDE_TYPE_DEBUGGER_CONTROLS,
+                                 "visible", TRUE,
+                                 NULL);
+  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);
+  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);
+
+  /* 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..ef7453e
--- /dev/null
+++ b/libide/debugger/ide-debugger-workbench-addin.h
@@ -0,0 +1,32 @@
+/* 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/>.
+ */
+
+#ifndef IDE_DEBUGGER_WORKBENCH_ADDIN_H
+#define IDE_DEBUGGER_WORKBENCH_ADDIN_H
+
+#include <ide.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
+
+#endif /* IDE_DEBUGGER_WORKBENCH_ADDIN_H */
diff --git a/libide/ide-context.c b/libide/ide-context.c
index 0d170e1..85c007e 100644
--- a/libide/ide-context.c
+++ b/libide/ide-context.c
@@ -35,6 +35,7 @@
 #include "buildsystem/ide-build-system.h"
 #include "buildsystem/ide-build-system-discovery.h"
 #include "buildsystem/ide-configuration-manager.h"
+#include "debugger/ide-debug-manager.h"
 #include "diagnostics/ide-diagnostics-manager.h"
 #include "devices/ide-device-manager.h"
 #include "doap/ide-doap.h"
@@ -68,6 +69,7 @@ struct _IdeContext
   IdeBuildSystem           *build_system;
   gchar                    *build_system_hint;
   IdeConfigurationManager  *configuration_manager;
+  IdeDebugManager          *debug_manager;
   IdeDiagnosticsManager    *diagnostics_manager;
   IdeDeviceManager         *device_manager;
   IdeDoap                  *doap;
@@ -816,6 +818,10 @@ ide_context_init (IdeContext *self)
                                             "context", self,
                                             NULL);
 
+  self->debug_manager = g_object_new (IDE_TYPE_DEBUG_MANAGER,
+                                      "context", self,
+                                      NULL);
+
   self->device_manager = g_object_new (IDE_TYPE_DEVICE_MANAGER,
                                        "context", self,
                                        NULL);
@@ -2390,3 +2396,19 @@ ide_context_get_diagnostics_manager (IdeContext *self)
 
   return self->diagnostics_manager;
 }
+
+/**
+ * ide_context_get_debug_manager:
+ * @self: An #IdeContext
+ *
+ * Gets the debug manager for the context.
+ *
+ * Returns: (transfer none): An #IdeDebugManager
+ */
+IdeDebugManager *
+ide_context_get_debug_manager (IdeContext *self)
+{
+  g_return_val_if_fail (IDE_IS_CONTEXT (self), NULL);
+
+  return self->debug_manager;
+}
diff --git a/libide/ide-context.h b/libide/ide-context.h
index c180226..2d1e520 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);
 IdeProject               *ide_context_get_project               (IdeContext           *self);
diff --git a/libide/ide-types.h b/libide/ide-types.h
index 93c889f..8643171 100644
--- a/libide/ide-types.h
+++ b/libide/ide-types.h
@@ -50,6 +50,9 @@ typedef struct _IdeConfigurationManager        IdeConfigurationManager;
 
 typedef struct _IdeContext                     IdeContext;
 
+typedef struct _IdeDebugger                    IdeDebugger;
+typedef struct _IdeDebugManager                IdeDebugManager;
+
 typedef struct _IdeDevice                      IdeDevice;
 typedef struct _IdeDeviceManager               IdeDeviceManager;
 typedef struct _IdeDeviceProvider              IdeDeviceProvider;
diff --git a/libide/resources/libide.gresource.xml b/libide/resources/libide.gresource.xml
index c6dd809..8d4d916 100644
--- a/libide/resources/libide.gresource.xml
+++ b/libide/resources/libide.gresource.xml
@@ -51,6 +51,8 @@
   </gresource>
 
   <gresource prefix="/org/gnome/builder/ui">
+    <file compressed="true" alias="ide-debugger-controls.ui">../debugger/ide-debugger-controls.ui</file>
+    <file compressed="true" 
alias="ide-debugger-perspective.ui">../debugger/ide-debugger-perspective.ui</file>
     <file compressed="true" alias="ide-editor-frame.ui">../editor/ide-editor-frame.ui</file>
     <file compressed="true" 
alias="ide-editor-layout-stack-controls.ui">../editor/ide-editor-layout-stack-controls.ui</file>
     <file compressed="true" alias="ide-editor-perspective.ui">../editor/ide-editor-perspective.ui</file>
@@ -90,14 +92,18 @@
     <file alias="buildconfig.plugin">../buildconfig/buildconfig.plugin</file>
   </gresource>
 
-  <gresource prefix="/org/gnome/builder/plugins/editor">
-    <file alias="editor.plugin">../editor/editor.plugin</file>
+  <gresource prefix="/org/gnome/builder/plugins/debugger">
+    <file alias="debugger.plugin">../debugger/debugger.plugin</file>
   </gresource>
 
   <gresource prefix="/org/gnome/builder/plugins/directory">
     <file alias="directory.plugin">../directory/directory.plugin</file>
   </gresource>
 
+  <gresource prefix="/org/gnome/builder/plugins/editor">
+    <file alias="editor.plugin">../editor/editor.plugin</file>
+  </gresource>
+
   <gresource prefix="/org/gnome/builder/plugins/webkit">
     <file alias="webkit.plugin">../webkit/webkit.plugin</file>
   </gresource>


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