[gnome-builder: 49/139] libide-editor: add libide-editor static library



commit 931fa2eaf6b671cabacc7b543ab282133daa62b0
Author: Christian Hergert <chergert redhat com>
Date:   Wed Jan 9 16:40:51 2019 -0800

    libide-editor: add libide-editor static library
    
    This creates a new libide-editor static library and moves a number of
    components into a separate plugin. The plugin was previous included
    directly in libide, and is no longer the case (now that there is no more
    libide shared library).
    
    Various components have been renamed to follow our naming semantics such
    as Page, Pane, and Panel.

 src/libide/editor/editor.plugin                    |   9 -
 src/libide/editor/gtk/menus.ui                     | 135 ---
 src/libide/editor/ide-editor-addin.c               |  73 +-
 src/libide/editor/ide-editor-addin.h               |  38 +-
 src/libide/editor/ide-editor-layout-stack-addin.c  | 115 ---
 ...or-view-actions.c => ide-editor-page-actions.c} | 361 ++++----
 src/libide/editor/ide-editor-page-addin.c          | 113 +++
 ...editor-view-addin.h => ide-editor-page-addin.h} |  49 +-
 ...-view-settings.c => ide-editor-page-settings.c} |  26 +-
 ...iew-shortcuts.c => ide-editor-page-shortcuts.c} |  56 +-
 .../{ide-editor-view.c => ide-editor-page.c}       | 562 ++++++------
 src/libide/editor/ide-editor-page.h                |  82 ++
 .../{ide-editor-view.ui => ide-editor-page.ui}     |   6 +-
 src/libide/editor/ide-editor-perspective.c         | 967 ---------------------
 src/libide/editor/ide-editor-perspective.h         |  61 --
 src/libide/editor/ide-editor-plugin-private.h      |  27 +
 src/libide/editor/ide-editor-plugin.c              |  47 -
 src/libide/editor/ide-editor-print-operation.c     |   4 +-
 src/libide/editor/ide-editor-print-operation.h     |   4 +-
 src/libide/editor/ide-editor-private.h             |  61 +-
 src/libide/editor/ide-editor-properties.c          | 445 ----------
 src/libide/editor/ide-editor-properties.ui         | 334 -------
 .../editor/ide-editor-search-bar-shortcuts.c       |   4 +-
 src/libide/editor/ide-editor-search-bar.c          |   9 +-
 src/libide/editor/ide-editor-search-bar.h          |   2 +-
 src/libide/editor/ide-editor-search.c              |  32 +-
 src/libide/editor/ide-editor-search.h              |   9 +-
 src/libide/editor/ide-editor-settings-dialog.c     | 331 +++++++
 src/libide/editor/ide-editor-settings-dialog.h     |  34 +
 src/libide/editor/ide-editor-settings-dialog.ui    | 288 ++++++
 src/libide/editor/ide-editor-sidebar.c             |  48 +-
 src/libide/editor/ide-editor-sidebar.h             |   9 +-
 src/libide/editor/ide-editor-sidebar.ui            |   2 +-
 ...tive-actions.c => ide-editor-surface-actions.c} | 101 ++-
 ...-shortcuts.c => ide-editor-surface-shortcuts.c} |  16 +-
 src/libide/editor/ide-editor-surface.c             | 919 ++++++++++++++++++++
 src/libide/editor/ide-editor-surface.h             |  65 ++
 ...editor-perspective.ui => ide-editor-surface.ui} |  15 +-
 src/libide/editor/ide-editor-utilities.c           |   6 +-
 src/libide/editor/ide-editor-utilities.h           |   9 +-
 src/libide/editor/ide-editor-view-addin.c          | 113 ---
 src/libide/editor/ide-editor-view.h                |  78 --
 src/libide/editor/ide-editor-workbench-addin.c     | 487 -----------
 src/libide/editor/ide-editor-workspace.c           | 110 +++
 ...-editor-properties.h => ide-editor-workspace.h} |  20 +-
 src/libide/editor/ide-editor-workspace.ui          |  55 ++
 src/libide/editor/libide-editor.gresource.xml      |  11 +
 src/libide/editor/libide-editor.h                  |  41 +
 src/libide/editor/meson.build                      | 137 ++-
 src/plugins/editor/default.css                     |  60 ++
 src/plugins/editor/editor-plugin.c                 |  56 ++
 src/plugins/editor/editor.gresource.xml            |  12 +
 src/plugins/editor/editor.plugin                   |  11 +
 src/plugins/editor/gbp-editor-application-addin.c  | 199 +++++
 src/plugins/editor/gbp-editor-application-addin.h  |  31 +
 src/plugins/editor/gbp-editor-frame-addin.c        | 115 +++
 .../editor/gbp-editor-frame-addin.h}               |   8 +-
 .../editor/gbp-editor-frame-controls.c}            | 158 ++--
 .../editor/gbp-editor-frame-controls.h}            |  18 +-
 .../editor/gbp-editor-frame-controls.ui}           |   2 +-
 .../editor/gbp-editor-hover-provider.c}            |  42 +-
 .../editor/gbp-editor-hover-provider.h}            |   8 +-
 .../editor/gbp-editor-session-addin.c}             | 228 ++---
 .../editor/gbp-editor-session-addin.h}             |   8 +-
 src/plugins/editor/gbp-editor-workbench-addin.c    | 341 ++++++++
 .../editor/gbp-editor-workbench-addin.h}           |   8 +-
 src/plugins/editor/gbp-editor-workspace-addin.c    | 317 +++++++
 src/plugins/editor/gbp-editor-workspace-addin.h    |  31 +
 src/plugins/editor/gtk/menus.ui                    | 171 ++++
 src/plugins/editor/meson.build                     |  18 +
 src/plugins/editor/shared.css                      |  24 +
 71 files changed, 4578 insertions(+), 3814 deletions(-)
---
diff --git a/src/libide/editor/ide-editor-addin.c b/src/libide/editor/ide-editor-addin.c
index 93b8b7b52..b2873d0f2 100644
--- a/src/libide/editor/ide-editor-addin.c
+++ b/src/libide/editor/ide-editor-addin.c
@@ -22,22 +22,22 @@
 
 #include "config.h"
 
-#include "editor/ide-editor-addin.h"
-#include "editor/ide-editor-private.h"
+#include "ide-editor-addin.h"
+#include "ide-editor-private.h"
 
 /**
  * SECTION:ide-editor-addin
  * @title: IdeEditorAddin
- * @short_description: Addins for the editor perspective
+ * @short_description: Addins for the editor surface
  *
  * The #IdeEditorAddin interface provides a simplified interface for
  * plugins that want to perform operations in, or extend, the editor
- * perspective.
+ * surface.
  *
  * This differs from the #IdeWorkbenchAddin in that you are given access
- * to the editor perspective directly. This can be convenient if all you
- * need to do is add panels or perform view tracking of the current
- * focus view.
+ * to the editor surface directly. This can be convenient if all you
+ * need to do is add panels or perform page tracking of the current
+ * focus page.
  *
  * Since: 3.32
  */
@@ -52,7 +52,7 @@ ide_editor_addin_default_init (IdeEditorAddinInterface *iface)
 /**
  * ide_editor_addin_load:
  * @self: an #IdeEditorAddin
- * @perspective: an #IdeEditorPeprsective
+ * @surface: an #IdeEditorPeprsective
  *
  * This method is called to load the addin.
  *
@@ -61,20 +61,20 @@ ide_editor_addin_default_init (IdeEditorAddinInterface *iface)
  * Since: 3.32
  */
 void
-ide_editor_addin_load (IdeEditorAddin       *self,
-                       IdeEditorPerspective *perspective)
+ide_editor_addin_load (IdeEditorAddin   *self,
+                       IdeEditorSurface *surface)
 {
   g_return_if_fail (IDE_IS_EDITOR_ADDIN (self));
-  g_return_if_fail (IDE_IS_EDITOR_PERSPECTIVE (perspective));
+  g_return_if_fail (IDE_IS_EDITOR_SURFACE (surface));
 
   if (IDE_EDITOR_ADDIN_GET_IFACE (self)->load)
-    IDE_EDITOR_ADDIN_GET_IFACE (self)->load (self, perspective);
+    IDE_EDITOR_ADDIN_GET_IFACE (self)->load (self, surface);
 }
 
 /**
  * ide_editor_addin_unload:
  * @self: an #IdeEditorAddin
- * @perspective: an #IdeEditorPerspective
+ * @surface: an #IdeEditorSurface
  *
  * This method is called to unload the addin.
  *
@@ -84,52 +84,52 @@ ide_editor_addin_load (IdeEditorAddin       *self,
  * Since: 3.32
  */
 void
-ide_editor_addin_unload (IdeEditorAddin       *self,
-                         IdeEditorPerspective *perspective)
+ide_editor_addin_unload (IdeEditorAddin   *self,
+                         IdeEditorSurface *surface)
 {
   g_return_if_fail (IDE_IS_EDITOR_ADDIN (self));
-  g_return_if_fail (IDE_IS_EDITOR_PERSPECTIVE (perspective));
+  g_return_if_fail (IDE_IS_EDITOR_SURFACE (surface));
 
   if (IDE_EDITOR_ADDIN_GET_IFACE (self)->unload)
-    IDE_EDITOR_ADDIN_GET_IFACE (self)->unload (self, perspective);
+    IDE_EDITOR_ADDIN_GET_IFACE (self)->unload (self, surface);
 }
 
 /**
- * ide_editor_addin_view_set:
+ * ide_editor_addin_page_set:
  * @self: an #IdeEditorAddin
- * @view: (nullable): an #IdeLayoutView or %NULL
+ * @page: (nullable): an #IdePage or %NULL
  *
- * This function is called when the current view has changed in the
- * editor perspective. This could happen when the user focus another
- * view, either with the keyboard, mouse, touch, or by opening a new
+ * This function is called when the current page has changed in the
+ * editor surface. This could happen when the user focus another
+ * page, either with the keyboard, mouse, touch, or by opening a new
  * buffer.
  *
- * Note that @view may not be an #IdeEditorView, so consumers of this
+ * Note that @page may not be an #IdeEditorView, so consumers of this
  * interface should take appropriate action based on the type.
  *
- * When the last view is removed, @view will be %NULL to indicate to the
- * addin that there is no active view.
+ * When the last page is removed, @page will be %NULL to indicate to the
+ * addin that there is no active page.
  *
  * Since: 3.32
  */
 void
-ide_editor_addin_view_set (IdeEditorAddin *self,
-                           IdeLayoutView  *view)
+ide_editor_addin_page_set (IdeEditorAddin *self,
+                           IdePage        *page)
 {
   g_return_if_fail (IDE_IS_EDITOR_ADDIN (self));
-  g_return_if_fail (!view || IDE_IS_LAYOUT_VIEW (view));
+  g_return_if_fail (!page || IDE_IS_PAGE (page));
 
-  if (IDE_EDITOR_ADDIN_GET_IFACE (self)->view_set)
-    IDE_EDITOR_ADDIN_GET_IFACE (self)->view_set (self, view);
+  if (IDE_EDITOR_ADDIN_GET_IFACE (self)->page_set)
+    IDE_EDITOR_ADDIN_GET_IFACE (self)->page_set (self, page);
 }
 
 /**
  * ide_editor_addin_find_by_module_name:
- * @editor: an #IdeEditorPerspective
+ * @editor: an #IdeEditorSurface
  * @module_name: the module name of the addin
  *
  * This function allows locating an #IdeEditorAddin that is attached
- * to the #IdeEditorPerspective by the addin module name. The module name
+ * to the #IdeEditorSurface by the addin module name. The module name
  * should match the value specified in the ".plugin" module definition.
  *
  * Returns: (transfer none) (nullable): An #IdeEditorAddin or %NULL
@@ -137,15 +137,18 @@ ide_editor_addin_view_set (IdeEditorAddin *self,
  * Since: 3.32
  */
 IdeEditorAddin *
-ide_editor_addin_find_by_module_name (IdeEditorPerspective *editor,
-                                      const gchar          *module_name)
+ide_editor_addin_find_by_module_name (IdeEditorSurface *editor,
+                                      const gchar      *module_name)
 {
   PeasExtension *ret = NULL;
   PeasPluginInfo *plugin_info;
 
-  g_return_val_if_fail (IDE_IS_EDITOR_PERSPECTIVE (editor), NULL);
+  g_return_val_if_fail (IDE_IS_EDITOR_SURFACE (editor), NULL);
   g_return_val_if_fail (module_name != NULL, NULL);
 
+  if (editor->addins == NULL)
+    return NULL;
+
   plugin_info = peas_engine_get_plugin_info (peas_engine_get_default (), module_name);
 
   if (plugin_info != NULL)
diff --git a/src/libide/editor/ide-editor-addin.h b/src/libide/editor/ide-editor-addin.h
index b01610e71..eaaaa9f5d 100644
--- a/src/libide/editor/ide-editor-addin.h
+++ b/src/libide/editor/ide-editor-addin.h
@@ -20,10 +20,14 @@
 
 #pragma once
 
-#include "ide-version-macros.h"
+#if !defined (IDE_EDITOR_INSIDE) && !defined (IDE_EDITOR_COMPILATION)
+# error "Only <libide-editor.h> can be included directly."
+#endif
 
-#include "editor/ide-editor-perspective.h"
-#include "layout/ide-layout-view.h"
+#include <libide-core.h>
+#include <libide-gui.h>
+
+#include "ide-editor-surface.h"
 
 G_BEGIN_DECLS
 
@@ -36,26 +40,26 @@ struct _IdeEditorAddinInterface
 {
   GTypeInterface parent_iface;
 
-  void (*load)     (IdeEditorAddin       *self,
-                    IdeEditorPerspective *perspective);
-  void (*unload)   (IdeEditorAddin       *self,
-                    IdeEditorPerspective *perspective);
-  void (*view_set) (IdeEditorAddin       *self,
-                    IdeLayoutView        *view);
+  void (*load)     (IdeEditorAddin   *self,
+                    IdeEditorSurface *surface);
+  void (*unload)   (IdeEditorAddin   *self,
+                    IdeEditorSurface *surface);
+  void (*page_set) (IdeEditorAddin   *self,
+                    IdePage          *page);
 };
 
 IDE_AVAILABLE_IN_3_32
-void ide_editor_addin_load     (IdeEditorAddin       *self,
-                                IdeEditorPerspective *perspective);
+void ide_editor_addin_load     (IdeEditorAddin   *self,
+                                IdeEditorSurface *surface);
 IDE_AVAILABLE_IN_3_32
-void ide_editor_addin_unload   (IdeEditorAddin       *self,
-                                IdeEditorPerspective *perspective);
+void ide_editor_addin_unload   (IdeEditorAddin   *self,
+                                IdeEditorSurface *surface);
 IDE_AVAILABLE_IN_3_32
-void ide_editor_addin_view_set (IdeEditorAddin       *self,
-                                IdeLayoutView        *view);
+void ide_editor_addin_page_set (IdeEditorAddin   *self,
+                                IdePage          *page);
 
 IDE_AVAILABLE_IN_3_32
-IdeEditorAddin *ide_editor_addin_find_by_module_name (IdeEditorPerspective *editor,
-                                                      const gchar          *module_name);
+IdeEditorAddin *ide_editor_addin_find_by_module_name (IdeEditorSurface *editor,
+                                                      const gchar      *module_name);
 
 G_END_DECLS
diff --git a/src/libide/editor/ide-editor-view-actions.c b/src/libide/editor/ide-editor-page-actions.c
similarity index 57%
rename from src/libide/editor/ide-editor-view-actions.c
rename to src/libide/editor/ide-editor-page-actions.c
index 017df3ca2..cff2d6e89 100644
--- a/src/libide/editor/ide-editor-view-actions.c
+++ b/src/libide/editor/ide-editor-page-actions.c
@@ -1,4 +1,4 @@
-/* ide-editor-view-actions.c
+/* ide-editor-page-actions.c
  *
  * Copyright 2017-2019 Christian Hergert <chergert redhat com>
  *
@@ -18,96 +18,90 @@
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
 
-#define G_LOG_DOMAIN "ide-editor-view-actions"
+#define G_LOG_DOMAIN "ide-editor-page-actions"
 
 #include "config.h"
 
 #include <glib/gi18n.h>
+#include <libide-code.h>
+#include <libide-gui.h>
 
-#include "files/ide-file.h"
-#include "files/ide-file-settings.h"
-#include "buffers/ide-buffer.h"
-#include "buffers/ide-buffer-manager.h"
-#include "editor/ide-editor-perspective.h"
-#include "editor/ide-editor-private.h"
-#include "layout/ide-layout-transient-sidebar.h"
-#include "editor/ide-editor-print-operation.h"
-#include "util/ide-progress.h"
-#include "vcs/ide-vcs.h"
+#include "ide-editor-surface.h"
+#include "ide-editor-private.h"
+#include "ide-editor-print-operation.h"
+#include "ide-editor-settings-dialog.h"
 
 static void
-ide_editor_view_actions_reload_cb (GObject      *object,
+ide_editor_page_actions_reload_cb (GObject      *object,
                                    GAsyncResult *result,
                                    gpointer      user_data)
 {
-  IdeBufferManager *buffer_manager = (IdeBufferManager *)object;
-  g_autoptr(IdeEditorView) self = user_data;
+  IdeBufferManager *bufmgr = (IdeBufferManager *)object;
+  g_autoptr(IdeEditorPage) self = user_data;
   g_autoptr(IdeBuffer) buffer = NULL;
   g_autoptr(GError) error = NULL;
 
-  g_assert (IDE_IS_BUFFER_MANAGER (buffer_manager));
+  g_assert (IDE_IS_BUFFER_MANAGER (bufmgr));
   g_assert (G_IS_ASYNC_RESULT (result));
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
 
   if (self->progress_bar != NULL)
     dzl_gtk_widget_hide_with_fade (GTK_WIDGET (self->progress_bar));
 
-  if (!(buffer = ide_buffer_manager_load_file_finish (buffer_manager, result, &error)))
+  if (!(buffer = ide_buffer_manager_load_file_finish (bufmgr, result, &error)))
     {
       g_warning ("%s", error->message);
-      ide_layout_view_report_error (IDE_LAYOUT_VIEW (self),
-                                    /* translators: %s is the error message */
-                                    _("Failed to load file: %s"), error->message);
-      ide_layout_view_set_failed (IDE_LAYOUT_VIEW (self), TRUE);
+      ide_page_report_error (IDE_PAGE (self),
+                             /* translators: %s is the error message */
+                             _("Failed to load file: %s"), error->message);
+      ide_page_set_failed (IDE_PAGE (self), TRUE);
     }
   else
     {
-      ide_editor_view_scroll_to_line (self, 0);
+      ide_editor_page_scroll_to_line (self, 0);
     }
 }
 
 static void
-ide_editor_view_actions_reload (GSimpleAction *action,
+ide_editor_page_actions_reload (GSimpleAction *action,
                                 GVariant      *param,
                                 gpointer       user_data)
 {
-  IdeEditorView *self = user_data;
-  g_autoptr(IdeProgress) progress = NULL;
-  IdeBufferManager *buffer_manager;
-  IdeContext *context;
+  IdeEditorPage *self = user_data;
+  g_autoptr(IdeNotification) notif = NULL;
+  g_autoptr(IdeContext) context = NULL;
+  IdeBufferManager *bufmgr;
   IdeBuffer *buffer;
-  IdeFile *file;
+  GFile *file;
 
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
 
-  buffer = ide_editor_view_get_buffer (self);
-  context = ide_buffer_get_context (buffer);
-  buffer_manager = ide_context_get_buffer_manager (context);
+  buffer = ide_editor_page_get_buffer (self);
+  context = ide_buffer_ref_context (buffer);
+  bufmgr = ide_buffer_manager_from_context (context);
   file = ide_buffer_get_file (buffer);
 
   gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (self->progress_bar), 0.0);
   gtk_widget_show (GTK_WIDGET (self->progress_bar));
 
-  ide_buffer_manager_load_file_async (buffer_manager,
+  ide_buffer_manager_load_file_async (bufmgr,
                                       file,
-                                      TRUE,
-                                      IDE_WORKBENCH_OPEN_FLAGS_NONE,
-                                      &progress,
+                                      IDE_BUFFER_OPEN_FLAGS_FORCE_RELOAD,
                                       NULL,
-                                      ide_editor_view_actions_reload_cb,
+                                      &notif,
+                                      ide_editor_page_actions_reload_cb,
                                       g_object_ref (self));
 
-  g_object_bind_property (progress, "fraction",
-                          self->progress_bar, "fraction",
+  g_object_bind_property (notif, "progress", self->progress_bar, "fraction",
                           G_BINDING_SYNC_CREATE);
 }
 
 static void
-handle_print_result (IdeEditorView           *self,
+handle_print_result (IdeEditorPage           *self,
                      GtkPrintOperation       *operation,
                      GtkPrintOperationResult  result)
 {
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
   g_assert (GTK_IS_PRINT_OPERATION (operation));
 
   if (result == GTK_PRINT_OPERATION_RESULT_ERROR)
@@ -117,9 +111,9 @@ handle_print_result (IdeEditorView           *self,
       gtk_print_operation_get_error (operation, &error);
 
       g_warning ("%s", error->message);
-      ide_layout_view_report_error (IDE_LAYOUT_VIEW (self),
-                                    /* translators: %s is the error message */
-                                    _("Print failed: %s"), error->message);
+      ide_page_report_error (IDE_PAGE (self),
+                             /* translators: %s is the error message */
+                             _("Print failed: %s"), error->message);
     }
 }
 
@@ -128,10 +122,10 @@ print_done (GtkPrintOperation       *operation,
             GtkPrintOperationResult  result,
             gpointer                 user_data)
 {
-  IdeEditorView *self = user_data;
+  IdeEditorPage *self = user_data;
 
   g_assert (GTK_IS_PRINT_OPERATION (operation));
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
 
   handle_print_result (self, operation, result);
 
@@ -140,21 +134,21 @@ print_done (GtkPrintOperation       *operation,
 }
 
 static void
-ide_editor_view_actions_print (GSimpleAction *action,
+ide_editor_page_actions_print (GSimpleAction *action,
                                GVariant      *param,
                                gpointer       user_data)
 {
   g_autoptr(IdeEditorPrintOperation) operation = NULL;
-  IdeEditorView *self = user_data;
+  IdeEditorPage *self = user_data;
   IdeSourceView *source_view;
   GtkWidget *toplevel;
   GtkPrintOperationResult result;
 
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
 
   toplevel = gtk_widget_get_ancestor (GTK_WIDGET (self), GTK_TYPE_WINDOW);
 
-  source_view = ide_editor_view_get_view (self);
+  source_view = ide_editor_page_get_view (self);
   operation = ide_editor_print_operation_new (source_view);
 
   /* keep a ref until "done" is emitted */
@@ -173,25 +167,25 @@ ide_editor_view_actions_print (GSimpleAction *action,
 }
 
 static void
-ide_editor_view_actions_save_cb (GObject      *object,
+ide_editor_page_actions_save_cb (GObject      *object,
                                  GAsyncResult *result,
                                  gpointer      user_data)
 {
-  IdeBufferManager *bufmgr = (IdeBufferManager *)object;
-  g_autoptr(IdeEditorView) self = user_data;
+  IdeBuffer *buffer = (IdeBuffer *)object;
+  g_autoptr(IdeEditorPage) self = user_data;
   g_autoptr(GError) error = NULL;
 
-  g_assert (IDE_IS_BUFFER_MANAGER (bufmgr));
+  g_assert (IDE_IS_BUFFER (buffer));
   g_assert (G_IS_ASYNC_RESULT (result));
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
 
-  if (!ide_buffer_manager_save_file_finish (bufmgr, result, &error))
+  if (!ide_buffer_save_file_finish (buffer, result, &error))
     {
       g_warning ("%s", error->message);
-      ide_layout_view_report_error (IDE_LAYOUT_VIEW (self),
-                                    /* translators: %s is the error message */
-                                    _("Failed to save file: %s"), error->message);
-      ide_layout_view_set_failed (IDE_LAYOUT_VIEW (self), TRUE);
+      ide_page_report_error (IDE_PAGE (self),
+                             /* translators: %s is the error message */
+                             _("Failed to save file: %s"), error->message);
+      ide_page_set_failed (IDE_PAGE (self), TRUE);
     }
 
   if (self->progress_bar != NULL)
@@ -199,44 +193,40 @@ ide_editor_view_actions_save_cb (GObject      *object,
 }
 
 static void
-ide_editor_view_actions_save (GSimpleAction *action,
+ide_editor_page_actions_save (GSimpleAction *action,
                               GVariant      *variant,
                               gpointer       user_data)
 {
-  IdeEditorView *self = user_data;
-  IdeBufferManager *buffer_manager;
-  g_autoptr(IdeProgress) progress = NULL;
-  g_autoptr(IdeFile) local_file = NULL;
-  IdeContext *context;
+  IdeEditorPage *self = user_data;
+  IdeBufferManager *bufmgr;
+  g_autoptr(IdeNotification) notif = NULL;
+  g_autoptr(IdeContext) context = NULL;
+  g_autoptr(GFile) local_file = NULL;
+  g_autoptr(GFile) workdir = NULL;
   IdeBuffer *buffer;
-  IdeFile *file;
-  IdeVcs *vcs;
-  GFile *workdir;
+  GFile *file;
 
   g_assert (G_IS_SIMPLE_ACTION (action));
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
 
-  buffer = ide_editor_view_get_buffer (self);
+  buffer = ide_editor_page_get_buffer (self);
   g_return_if_fail (IDE_IS_BUFFER (buffer));
 
-  context = ide_buffer_get_context (buffer);
+  context = ide_buffer_ref_context (buffer);
   g_return_if_fail (IDE_IS_CONTEXT (context));
 
   file = ide_buffer_get_file (buffer);
-  g_return_if_fail (IDE_IS_FILE (file));
+  g_return_if_fail (G_IS_FILE (file));
 
-  buffer_manager = ide_context_get_buffer_manager (context);
-  vcs = ide_context_get_vcs (context);
-  workdir = ide_vcs_get_working_directory (vcs);
+  bufmgr = ide_buffer_manager_from_context (context);
+  workdir = ide_context_ref_workdir (context);
 
-  g_assert (IDE_IS_BUFFER_MANAGER (buffer_manager));
-  g_assert (IDE_IS_VCS (vcs));
+  g_assert (IDE_IS_BUFFER_MANAGER (bufmgr));
   g_assert (G_IS_FILE (workdir));
 
-  if (ide_file_get_is_temporary (file))
+  if (ide_buffer_get_is_temporary (buffer))
     {
       GtkFileChooserNative *dialog;
-      g_autoptr(GFile) gfile = NULL;
       GtkWidget *toplevel;
       gint ret;
 
@@ -260,10 +250,7 @@ ide_editor_view_actions_save (GSimpleAction *action,
       ret = gtk_native_dialog_run (GTK_NATIVE_DIALOG (dialog));
 
       if (ret == GTK_RESPONSE_ACCEPT)
-        {
-          gfile = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
-          file = local_file = ide_file_new (context, gfile);
-        }
+        file = local_file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
 
       gtk_native_dialog_destroy (GTK_NATIVE_DIALOG (dialog));
 
@@ -271,16 +258,14 @@ ide_editor_view_actions_save (GSimpleAction *action,
         return;
     }
 
-  ide_buffer_manager_save_file_async (buffer_manager,
-                                      buffer,
-                                      file,
-                                      &progress,
-                                      NULL,
-                                      ide_editor_view_actions_save_cb,
-                                      g_object_ref (self));
+  ide_buffer_save_file_async (buffer,
+                              file,
+                              NULL,
+                              &notif,
+                              ide_editor_page_actions_save_cb,
+                              g_object_ref (self));
 
-  g_object_bind_property (progress, "fraction",
-                          self->progress_bar, "fraction",
+  g_object_bind_property (notif, "progress", self->progress_bar, "fraction",
                           G_BINDING_SYNC_CREATE);
 
   gtk_widget_show (GTK_WIDGET (self->progress_bar));
@@ -288,62 +273,60 @@ ide_editor_view_actions_save (GSimpleAction *action,
 
 
 static void
-ide_editor_view_actions_save_as_cb (GObject      *object,
+ide_editor_page_actions_save_as_cb (GObject      *object,
                                     GAsyncResult *result,
                                     gpointer      user_data)
 {
-  IdeBufferManager *buffer_manager = (IdeBufferManager *)object;
-  g_autoptr(IdeEditorView) self = user_data;
+  IdeBuffer *buffer = (IdeBuffer *)object;
+  g_autoptr(IdeEditorPage) self = user_data;
   g_autoptr(GError) error = NULL;
 
-  g_assert (IDE_IS_BUFFER_MANAGER (buffer_manager));
+  g_assert (IDE_IS_BUFFER (buffer));
   g_assert (G_IS_ASYNC_RESULT (result));
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
 
-  if (!ide_buffer_manager_save_file_finish (buffer_manager, result, &error))
+  if (!ide_buffer_save_file_finish (buffer, result, &error))
     {
-      /* In this case, the editor view hasn't failed since this is for an
+      /* In this case, the editor page hasn't failed since this is for an
        * alternate file (which maybe we just don't have access to on the
        * network or something).
        *
        * But we do still need to notify the user of the error.
        */
       g_warning ("%s", error->message);
-      ide_layout_view_report_error (IDE_LAYOUT_VIEW (self),
-                                    /* translators: %s is the underlying error message */
-                                    _("Failed to save file: %s"),
-                                    error->message);
+      ide_page_report_error (IDE_PAGE (self),
+                             /* translators: %s is the underlying error message */
+                             _("Failed to save file: %s"),
+                             error->message);
     }
 
   dzl_gtk_widget_hide_with_fade (GTK_WIDGET (self->progress_bar));
 }
 
 static void
-ide_editor_view_actions_save_as (GSimpleAction *action,
+ide_editor_page_actions_save_as (GSimpleAction *action,
                                  GVariant      *param,
                                  gpointer       user_data)
 {
-  IdeEditorView *self = user_data;
+  IdeEditorPage *self = user_data;
   GtkFileChooserNative *dialog;
-  IdeContext *context;
   IdeBuffer *buffer;
   GtkWidget *toplevel;
-  IdeFile *file;
-  GFile *gfile;
+  GFile *file;
   gint ret;
 
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
 
-  buffer = ide_editor_view_get_buffer (self);
+  buffer = ide_editor_page_get_buffer (self);
   file = ide_buffer_get_file (buffer);
 
   /* Just redirect to the save flow if we have a temporary
    * file currently. That way we can avoid splitting the
    * flow to handle both cases here.
    */
-  if (ide_file_get_is_temporary (file))
+  if (ide_buffer_get_is_temporary (buffer))
     {
-      ide_editor_view_actions_save (action, NULL, user_data);
+      ide_editor_page_actions_save (action, NULL, user_data);
       return;
     }
 
@@ -358,35 +341,26 @@ ide_editor_view_actions_save_as (GSimpleAction *action,
   gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), FALSE);
   gtk_file_chooser_set_show_hidden (GTK_FILE_CHOOSER (dialog), FALSE);
 
-  context = ide_buffer_get_context (buffer);
-  gfile = ide_file_get_file (file);
-
-  if (gfile != NULL)
-    gtk_file_chooser_set_file (GTK_FILE_CHOOSER (dialog), gfile, NULL);
+  if (file != NULL)
+    gtk_file_chooser_set_file (GTK_FILE_CHOOSER (dialog), file, NULL);
 
   ret = gtk_native_dialog_run (GTK_NATIVE_DIALOG (dialog));
 
   if (ret == GTK_RESPONSE_ACCEPT)
     {
-      g_autoptr(GFile) target = NULL;
-      g_autoptr(IdeFile) save_as = NULL;
-      g_autoptr(IdeProgress) progress = NULL;
-      IdeBufferManager *buffer_manager;
-
-      target = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
-      save_as = ide_file_new (context, target);
-      buffer_manager = ide_context_get_buffer_manager (context);
-
-      ide_buffer_manager_save_file_async (buffer_manager,
-                                          buffer,
-                                          save_as,
-                                          &progress,
-                                          NULL,
-                                          ide_editor_view_actions_save_as_cb,
-                                          g_object_ref (self));
-
-      g_object_bind_property (progress, "fraction",
-                              self->progress_bar, "fraction",
+      g_autoptr(GFile) save_as = NULL;
+      g_autoptr(IdeNotification) notif = NULL;
+
+      save_as = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
+
+      ide_buffer_save_file_async (buffer,
+                                  save_as,
+                                  NULL,
+                                  &notif,
+                                  ide_editor_page_actions_save_as_cb,
+                                  g_object_ref (self));
+
+      g_object_bind_property (notif, "progress", self->progress_bar, "fraction",
                               G_BINDING_SYNC_CREATE);
 
       gtk_widget_show (GTK_WIDGET (self->progress_bar));
@@ -396,16 +370,16 @@ ide_editor_view_actions_save_as (GSimpleAction *action,
 }
 
 static void
-ide_editor_view_actions_find (GSimpleAction *action,
+ide_editor_page_actions_find (GSimpleAction *action,
                               GVariant      *variant,
                               gpointer       user_data)
 {
-  IdeEditorView *self = user_data;
+  IdeEditorPage *self = user_data;
   GtkTextIter begin;
   GtkTextIter end;
 
   g_assert (G_IS_SIMPLE_ACTION (action));
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
 
   if (gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (self->buffer), &begin, &end))
     {
@@ -419,16 +393,16 @@ ide_editor_view_actions_find (GSimpleAction *action,
 }
 
 static void
-ide_editor_view_actions_find_replace (GSimpleAction *action,
+ide_editor_page_actions_find_replace (GSimpleAction *action,
                                       GVariant      *variant,
                                       gpointer       user_data)
 {
-  IdeEditorView *self = user_data;
+  IdeEditorPage *self = user_data;
   GtkTextIter begin;
   GtkTextIter end;
 
   g_assert (G_IS_SIMPLE_ACTION (action));
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
 
   if (gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (self->buffer), &begin, &end))
     {
@@ -442,28 +416,28 @@ ide_editor_view_actions_find_replace (GSimpleAction *action,
 }
 
 static void
-ide_editor_view_actions_hide_search (GSimpleAction *action,
+ide_editor_page_actions_hide_search (GSimpleAction *action,
                                      GVariant      *variant,
                                      gpointer       user_data)
 {
-  IdeEditorView *self = user_data;
+  IdeEditorPage *self = user_data;
 
   g_assert (G_IS_SIMPLE_ACTION (action));
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
 
   gtk_revealer_set_reveal_child (self->search_revealer, FALSE);
   gtk_widget_grab_focus (GTK_WIDGET (self->source_view));
 }
 
 static void
-ide_editor_view_actions_notify_file_settings (IdeEditorView *self,
+ide_editor_page_actions_notify_file_settings (IdeEditorPage *self,
                                               GParamSpec    *pspec,
                                               IdeSourceView *source_view)
 {
   IdeFileSettings *file_settings;
   GActionGroup *group;
 
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
   g_assert (IDE_IS_SOURCE_VIEW (source_view));
 
   group = gtk_widget_get_action_group (GTK_WIDGET (self), "file-settings");
@@ -476,33 +450,33 @@ ide_editor_view_actions_notify_file_settings (IdeEditorView *self,
 }
 
 static void
-ide_editor_view_actions_move_next_error (GSimpleAction *action,
+ide_editor_page_actions_move_next_error (GSimpleAction *action,
                                          GVariant      *variant,
                                          gpointer       user_data)
 {
-  ide_editor_view_move_next_error (user_data);
+  ide_editor_page_move_next_error (user_data);
 }
 
 static void
-ide_editor_view_actions_move_previous_error (GSimpleAction *action,
+ide_editor_page_actions_move_previous_error (GSimpleAction *action,
                                              GVariant      *variant,
                                              gpointer       user_data)
 {
-  ide_editor_view_move_previous_error (user_data);
+  ide_editor_page_move_previous_error (user_data);
 }
 
 static void
-ide_editor_view_actions_activate_next_search_result (GSimpleAction *action,
+ide_editor_page_actions_activate_next_search_result (GSimpleAction *action,
                                                      GVariant      *variant,
                                                      gpointer       user_data)
 {
-  IdeEditorView *self = user_data;
+  IdeEditorPage *self = user_data;
   GtkTextIter begin;
   GtkTextIter end;
 
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
 
-  ide_editor_view_move_next_search_result (self);
+  ide_editor_page_move_next_search_result (self);
 
   gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (self->buffer), &begin, &end);
   gtk_widget_grab_focus (GTK_WIDGET (self->source_view));
@@ -511,76 +485,77 @@ ide_editor_view_actions_activate_next_search_result (GSimpleAction *action,
 }
 
 static void
-ide_editor_view_actions_move_next_search_result (GSimpleAction *action,
+ide_editor_page_actions_move_next_search_result (GSimpleAction *action,
                                                  GVariant      *variant,
                                                  gpointer       user_data)
 {
-  ide_editor_view_move_next_search_result (user_data);
+  ide_editor_page_move_next_search_result (user_data);
 }
 
 static void
-ide_editor_view_actions_move_previous_search_result (GSimpleAction *action,
+ide_editor_page_actions_move_previous_search_result (GSimpleAction *action,
                                                      GVariant      *variant,
                                                      gpointer       user_data)
 {
-  ide_editor_view_move_previous_search_result (user_data);
+  ide_editor_page_move_previous_search_result (user_data);
 }
 
 static void
-ide_editor_view_actions_properties (GSimpleAction *action,
+ide_editor_page_actions_properties (GSimpleAction *action,
                                     GVariant      *variant,
                                     gpointer       user_data)
 {
-  IdeEditorView *self = user_data;
-  GtkWidget *editor;
+  IdeEditorPage *self = user_data;
+  IdeEditorSettingsDialog *dialog;
 
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
 
-  editor = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_EDITOR_PERSPECTIVE);
-  _ide_editor_perspective_show_properties (IDE_EDITOR_PERSPECTIVE (editor), self);
+  dialog = ide_editor_settings_dialog_new (self);
+  g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
+  gtk_window_present (GTK_WINDOW (dialog));
 }
 
 static void
-ide_editor_view_actions_toggle_map (GSimpleAction *action,
+ide_editor_page_actions_toggle_map (GSimpleAction *action,
                                     GVariant      *variant,
                                     gpointer       user_data)
 {
-  IdeEditorView *self = user_data;
+  IdeEditorPage *self = user_data;
 
   g_assert (G_IS_SIMPLE_ACTION (action));
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
 
-  ide_editor_view_set_show_map (self, !ide_editor_view_get_show_map (self));
+  ide_editor_page_set_show_map (self, !ide_editor_page_get_show_map (self));
 }
 
 static const GActionEntry editor_view_entries[] = {
-  { "activate-next-search-result", ide_editor_view_actions_activate_next_search_result },
-  { "find", ide_editor_view_actions_find },
-  { "find-replace", ide_editor_view_actions_find_replace },
-  { "hide-search", ide_editor_view_actions_hide_search },
-  { "move-next-error", ide_editor_view_actions_move_next_error },
-  { "move-next-search-result", ide_editor_view_actions_move_next_search_result },
-  { "move-previous-error", ide_editor_view_actions_move_previous_error },
-  { "move-previous-search-result", ide_editor_view_actions_move_previous_search_result },
-  { "properties", ide_editor_view_actions_properties },
-  { "print", ide_editor_view_actions_print },
-  { "reload", ide_editor_view_actions_reload },
-  { "save", ide_editor_view_actions_save },
-  { "save-as", ide_editor_view_actions_save_as },
-  { "toggle-map", ide_editor_view_actions_toggle_map },
+  { "activate-next-search-result", ide_editor_page_actions_activate_next_search_result },
+  { "find", ide_editor_page_actions_find },
+  { "find-replace", ide_editor_page_actions_find_replace },
+  { "hide-search", ide_editor_page_actions_hide_search },
+  { "move-next-error", ide_editor_page_actions_move_next_error },
+  { "move-next-search-result", ide_editor_page_actions_move_next_search_result },
+  { "move-previous-error", ide_editor_page_actions_move_previous_error },
+  { "move-previous-search-result", ide_editor_page_actions_move_previous_search_result },
+  { "properties", ide_editor_page_actions_properties },
+  { "print", ide_editor_page_actions_print },
+  { "reload", ide_editor_page_actions_reload },
+  { "save", ide_editor_page_actions_save },
+  { "save-as", ide_editor_page_actions_save_as },
+  { "toggle-map", ide_editor_page_actions_toggle_map },
 };
 
 void
-_ide_editor_view_init_actions (IdeEditorView *self)
+_ide_editor_page_init_actions (IdeEditorPage *self)
 {
   g_autoptr(GSimpleActionGroup) group = NULL;
   g_autoptr(DzlPropertiesGroup) sv_props = NULL;
   g_autoptr(DzlPropertiesGroup) file_props = NULL;
   IdeSourceView *source_view;
 
-  g_return_if_fail (IDE_IS_EDITOR_VIEW (self));
+  g_return_if_fail (IDE_IS_EDITOR_PAGE (self));
 
-  source_view = ide_editor_view_get_view (self);
+  source_view = ide_editor_page_get_view (self);
 
   /* Setup our user-facing actions */
   group = g_simple_action_group_new ();
@@ -588,7 +563,7 @@ _ide_editor_view_init_actions (IdeEditorView *self)
                                    editor_view_entries,
                                    G_N_ELEMENTS (editor_view_entries),
                                    self);
-  gtk_widget_insert_action_group (GTK_WIDGET (self), "editor-view", G_ACTION_GROUP (group));
+  gtk_widget_insert_action_group (GTK_WIDGET (self), "editor-page", G_ACTION_GROUP (group));
 
   /* We want to access some settings properties as stateful GAction so they
    * manipulated using regular Gtk widgets from the properties panel.
@@ -610,15 +585,15 @@ _ide_editor_view_init_actions (IdeEditorView *self)
   dzl_properties_group_add_all_properties (file_props);
   g_signal_connect_swapped (source_view,
                             "notify::file-settings",
-                            G_CALLBACK (ide_editor_view_actions_notify_file_settings),
+                            G_CALLBACK (ide_editor_page_actions_notify_file_settings),
                             self);
   gtk_widget_insert_action_group (GTK_WIDGET (self), "file-settings", G_ACTION_GROUP (file_props));
-  ide_editor_view_actions_notify_file_settings (self, NULL, source_view);
+  ide_editor_page_actions_notify_file_settings (self, NULL, source_view);
 }
 
 void
-_ide_editor_view_update_actions (IdeEditorView *self)
+_ide_editor_page_update_actions (IdeEditorPage *self)
 {
-  g_return_if_fail (IDE_IS_EDITOR_VIEW (self));
+  g_return_if_fail (IDE_IS_EDITOR_PAGE (self));
 
 }
diff --git a/src/libide/editor/ide-editor-page-addin.c b/src/libide/editor/ide-editor-page-addin.c
new file mode 100644
index 000000000..5bb1d8476
--- /dev/null
+++ b/src/libide/editor/ide-editor-page-addin.c
@@ -0,0 +1,113 @@
+/* ide-editor-page-addin.c
+ *
+ * Copyright 2015-2019 Christian Hergert <christian hergert me>
+ *
+ * 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-editor-page-addin"
+
+#include "config.h"
+
+#include "ide-editor-private.h"
+#include "ide-editor-page-addin.h"
+
+G_DEFINE_INTERFACE (IdeEditorPageAddin, ide_editor_page_addin, G_TYPE_OBJECT)
+
+static void
+ide_editor_page_addin_default_init (IdeEditorPageAddinInterface *iface)
+{
+}
+
+void
+ide_editor_page_addin_load (IdeEditorPageAddin *self,
+                            IdeEditorPage      *page)
+{
+  g_return_if_fail (IDE_IS_EDITOR_PAGE_ADDIN (self));
+  g_return_if_fail (IDE_IS_EDITOR_PAGE (page));
+
+  if (IDE_EDITOR_PAGE_ADDIN_GET_IFACE (self)->load)
+    IDE_EDITOR_PAGE_ADDIN_GET_IFACE (self)->load (self, page);
+}
+
+void
+ide_editor_page_addin_unload (IdeEditorPageAddin *self,
+                              IdeEditorPage      *page)
+{
+  g_return_if_fail (IDE_IS_EDITOR_PAGE_ADDIN (self));
+  g_return_if_fail (IDE_IS_EDITOR_PAGE (page));
+
+  if (IDE_EDITOR_PAGE_ADDIN_GET_IFACE (self)->unload)
+    IDE_EDITOR_PAGE_ADDIN_GET_IFACE (self)->unload (self, page);
+}
+
+void
+ide_editor_page_addin_language_changed (IdeEditorPageAddin *self,
+                                        const gchar        *language_id)
+{
+  g_return_if_fail (IDE_IS_EDITOR_PAGE_ADDIN (self));
+
+  if (IDE_EDITOR_PAGE_ADDIN_GET_IFACE (self)->language_changed)
+    IDE_EDITOR_PAGE_ADDIN_GET_IFACE (self)->language_changed (self, language_id);
+}
+
+void
+ide_editor_page_addin_frame_set (IdeEditorPageAddin *self,
+                                 IdeFrame           *frame)
+{
+  g_return_if_fail (IDE_IS_EDITOR_PAGE_ADDIN (self));
+  g_return_if_fail (IDE_IS_FRAME (frame));
+
+  if (IDE_EDITOR_PAGE_ADDIN_GET_IFACE (self)->frame_set)
+    IDE_EDITOR_PAGE_ADDIN_GET_IFACE (self)->frame_set (self, frame);
+}
+
+/**
+ * ide_editor_page_addin_find_by_module_name:
+ * @page: an #IdeEditorPage
+ * @module_name: the module name which provides the addin
+ *
+ * This function will locate the #IdeEditorPageAddin that was registered
+ * by the addin named @module_name (which should match the module_name
+ * provided in the .plugin file).
+ *
+ * If no module was found or that module does not implement the
+ * #IdeEditorPageAddinInterface, then %NULL is returned.
+ *
+ * Returns: (transfer none) (nullable): An #IdeEditorPageAddin or %NULL
+ *
+ * Since: 3.32
+ */
+IdeEditorPageAddin *
+ide_editor_page_addin_find_by_module_name (IdeEditorPage *page,
+                                           const gchar   *module_name)
+{
+  PeasExtension *ret = NULL;
+  PeasPluginInfo *plugin_info;
+
+  g_return_val_if_fail (IDE_IS_EDITOR_PAGE (page), NULL);
+  g_return_val_if_fail (page->addins != NULL, NULL);
+  g_return_val_if_fail (module_name != NULL, NULL);
+
+  plugin_info = peas_engine_get_plugin_info (peas_engine_get_default (), module_name);
+
+  if (plugin_info != NULL)
+    ret = ide_extension_set_adapter_get_extension (page->addins, plugin_info);
+  else
+    g_warning ("No addin could be found matching module \"%s\"", module_name);
+
+  return ret ? IDE_EDITOR_PAGE_ADDIN (ret) : NULL;
+}
diff --git a/src/libide/editor/ide-editor-view-addin.h b/src/libide/editor/ide-editor-page-addin.h
similarity index 51%
rename from src/libide/editor/ide-editor-view-addin.h
rename to src/libide/editor/ide-editor-page-addin.h
index 7cbac8f67..4b929b7aa 100644
--- a/src/libide/editor/ide-editor-view-addin.h
+++ b/src/libide/editor/ide-editor-page-addin.h
@@ -1,4 +1,4 @@
-/* ide-editor-view-addin.h
+/* ide-editor-page-addin.h
  *
  * Copyright 2015-2019 Christian Hergert <christian hergert me>
  *
@@ -20,46 +20,51 @@
 
 #pragma once
 
-#include "ide-version-macros.h"
+#if !defined (IDE_EDITOR_INSIDE) && !defined (IDE_EDITOR_COMPILATION)
+# error "Only <libide-editor.h> can be included directly."
+#endif
 
-#include "editor/ide-editor-view.h"
-#include "layout/ide-layout-stack.h"
+
+#include <libide-core.h>
+#include <libide-gui.h>
+
+#include "ide-editor-page.h"
 
 G_BEGIN_DECLS
 
-#define IDE_TYPE_EDITOR_VIEW_ADDIN (ide_editor_view_addin_get_type ())
+#define IDE_TYPE_EDITOR_PAGE_ADDIN (ide_editor_page_addin_get_type ())
 
 IDE_AVAILABLE_IN_3_32
-G_DECLARE_INTERFACE (IdeEditorViewAddin, ide_editor_view_addin, IDE, EDITOR_VIEW_ADDIN, GObject)
+G_DECLARE_INTERFACE (IdeEditorPageAddin, ide_editor_page_addin, IDE, EDITOR_PAGE_ADDIN, GObject)
 
-struct _IdeEditorViewAddinInterface
+struct _IdeEditorPageAddinInterface
 {
   GTypeInterface parent;
 
-  void (*load)               (IdeEditorViewAddin *self,
-                              IdeEditorView      *view);
-  void (*unload)             (IdeEditorViewAddin *self,
-                              IdeEditorView      *view);
-  void (*language_changed)   (IdeEditorViewAddin *self,
+  void (*load)               (IdeEditorPageAddin *self,
+                              IdeEditorPage      *page);
+  void (*unload)             (IdeEditorPageAddin *self,
+                              IdeEditorPage      *page);
+  void (*language_changed)   (IdeEditorPageAddin *self,
                               const gchar        *language_id);
-  void (*stack_set)          (IdeEditorViewAddin *self,
-                              IdeLayoutStack     *stack);
+  void (*frame_set)          (IdeEditorPageAddin *self,
+                              IdeFrame           *frame);
 };
 
 IDE_AVAILABLE_IN_3_32
-void                ide_editor_view_addin_load                (IdeEditorViewAddin *self,
-                                                               IdeEditorView      *view);
+void                ide_editor_page_addin_load                (IdeEditorPageAddin *self,
+                                                               IdeEditorPage      *page);
 IDE_AVAILABLE_IN_3_32
-void                ide_editor_view_addin_unload              (IdeEditorViewAddin *self,
-                                                               IdeEditorView      *view);
+void                ide_editor_page_addin_unload              (IdeEditorPageAddin *self,
+                                                               IdeEditorPage      *page);
 IDE_AVAILABLE_IN_3_32
-void                ide_editor_view_addin_stack_set           (IdeEditorViewAddin *self,
-                                                               IdeLayoutStack     *stack);
+void                ide_editor_page_addin_frame_set           (IdeEditorPageAddin *self,
+                                                               IdeFrame           *frame);
 IDE_AVAILABLE_IN_3_32
-void                ide_editor_view_addin_language_changed    (IdeEditorViewAddin *self,
+void                ide_editor_page_addin_language_changed    (IdeEditorPageAddin *self,
                                                                const gchar        *language_id);
 IDE_AVAILABLE_IN_3_32
-IdeEditorViewAddin *ide_editor_view_addin_find_by_module_name (IdeEditorView      *view,
+IdeEditorPageAddin *ide_editor_page_addin_find_by_module_name (IdeEditorPage      *page,
                                                                const gchar        *module_name);
 
 G_END_DECLS
diff --git a/src/libide/editor/ide-editor-view-settings.c b/src/libide/editor/ide-editor-page-settings.c
similarity index 92%
rename from src/libide/editor/ide-editor-view-settings.c
rename to src/libide/editor/ide-editor-page-settings.c
index ed7da740b..c542e4d58 100644
--- a/src/libide/editor/ide-editor-view-settings.c
+++ b/src/libide/editor/ide-editor-page-settings.c
@@ -1,4 +1,4 @@
-/* ide-editor-view-settings.c
+/* ide-editor-page-settings.c
  *
  * Copyright 2017-2019 Christian Hergert <chergert redhat com>
  *
@@ -18,11 +18,11 @@
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
 
-#define G_LOG_DOMAIN "ide-editor-view-settings"
+#define G_LOG_DOMAIN "ide-editor-page-settings"
 
 #include "config.h"
 
-#include "editor/ide-editor-private.h"
+#include "ide-editor-private.h"
 
 #include <gtksourceview/gtksource.h>
 
@@ -51,17 +51,17 @@ get_wrap_mode (GValue   *value,
 }
 
 static void
-on_keybindings_changed (IdeEditorView *self,
+on_keybindings_changed (IdeEditorPage *self,
                         const gchar   *key,
                         GSettings     *settings)
 {
   IdeSourceView *source_view;
 
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
   g_assert (g_strcmp0 (key, "keybindings") == 0);
   g_assert (G_IS_SETTINGS (settings));
 
-  source_view = ide_editor_view_get_view (self);
+  source_view = ide_editor_page_get_view (self);
 
   g_signal_emit_by_name (source_view,
                          "set-mode",
@@ -70,7 +70,7 @@ on_keybindings_changed (IdeEditorView *self,
 }
 
 static void
-on_draw_spaces_changed (IdeEditorView *self,
+on_draw_spaces_changed (IdeEditorPage *self,
                         const gchar   *key,
                         GSettings     *settings)
 {
@@ -80,11 +80,11 @@ on_draw_spaces_changed (IdeEditorView *self,
   GtkSourceSpaceLocationFlags location_flags = GTK_SOURCE_SPACE_LOCATION_NONE;
   GtkSourceSpaceTypeFlags type_flags = GTK_SOURCE_SPACE_TYPE_NONE;
 
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
   g_assert (g_strcmp0 (key, "draw-spaces") == 0);
   g_assert (G_IS_SETTINGS (settings));
 
-  source_view = GTK_SOURCE_VIEW (ide_editor_view_get_view (self));
+  source_view = GTK_SOURCE_VIEW (ide_editor_page_get_view (self));
   drawer = gtk_source_view_get_space_drawer (source_view);
   flags = g_settings_get_flags (settings, "draw-spaces");
 
@@ -129,17 +129,17 @@ on_draw_spaces_changed (IdeEditorView *self,
 }
 
 void
-_ide_editor_view_init_settings (IdeEditorView *self)
+_ide_editor_page_init_settings (IdeEditorPage *self)
 {
   IdeSourceView *source_view;
   IdeBuffer *buffer;
 
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
   g_assert (self->editor_settings == NULL);
   g_assert (self->insight_settings == NULL);
 
-  source_view = ide_editor_view_get_view (self);
-  buffer = ide_editor_view_get_buffer (self);
+  source_view = ide_editor_page_get_view (self);
+  buffer = ide_editor_page_get_buffer (self);
 
   self->editor_settings = g_settings_new ("org.gnome.builder.editor");
 
diff --git a/src/libide/editor/ide-editor-view-shortcuts.c b/src/libide/editor/ide-editor-page-shortcuts.c
similarity index 78%
rename from src/libide/editor/ide-editor-view-shortcuts.c
rename to src/libide/editor/ide-editor-page-shortcuts.c
index 3de862b56..a73e81f6f 100644
--- a/src/libide/editor/ide-editor-view-shortcuts.c
+++ b/src/libide/editor/ide-editor-page-shortcuts.c
@@ -1,4 +1,4 @@
-/* ide-editor-view-shortcuts.c
+/* ide-editor-page-shortcuts.c
  *
  * Copyright 2017-2019 Christian Hergert <chergert redhat com>
  *
@@ -23,54 +23,54 @@
 #include <dazzle.h>
 #include <glib/gi18n.h>
 
-#include "editor/ide-editor-private.h"
+#include "ide-editor-private.h"
 
 #define I_(s) (g_intern_static_string(s))
 
 static DzlShortcutEntry editor_view_shortcuts[] = {
-  { "org.gnome.builder.editor-view.save",
+  { "org.gnome.builder.editor-page.save",
     0, NULL,
     NC_("shortcut window", "Editor shortcuts"),
     NC_("shortcut window", "Files"),
     NC_("shortcut window", "Save the document") },
 
-  { "org.gnome.builder.editor-view.save-as",
+  { "org.gnome.builder.editor-page.save-as",
     0, NULL,
     NC_("shortcut window", "Editor shortcuts"),
     NC_("shortcut window", "Files"),
     NC_("shortcut window", "Save the document with a new name") },
 
-  { "org.gnome.builder.editor-view.find",
+  { "org.gnome.builder.editor-page.find",
     0, NULL,
     NC_("shortcut window", "Editor shortcuts"),
     NC_("shortcut window", "Find and replace"),
     NC_("shortcut window", "Find") },
 
-  { "org.gnome.builder.editor-view.find-replace",
+  { "org.gnome.builder.editor-page.find-replace",
     0, NULL,
     NC_("shortcut window", "Editor shortcuts"),
     NC_("shortcut window", "Find and replace"),
     NC_("shortcut window", "Find and replace") },
 
-  { "org.gnome.builder.editor-view.next-match",
+  { "org.gnome.builder.editor-page.next-match",
     0, NULL,
     NC_("shortcut window", "Editor shortcuts"),
     NC_("shortcut window", "Find and replace"),
     NC_("shortcut window", "Move to the next match") },
 
-  { "org.gnome.builder.editor-view.prev-match",
+  { "org.gnome.builder.editor-page.prev-match",
     0, NULL,
     NC_("shortcut window", "Editor shortcuts"),
     NC_("shortcut window", "Find and replace"),
     NC_("shortcut window", "Move to the previous match") },
 
-  { "org.gnome.builder.editor-view.next-error",
+  { "org.gnome.builder.editor-page.next-error",
     0, NULL,
     NC_("shortcut window", "Editor shortcuts"),
     NC_("shortcut window", "Find and replace"),
     NC_("shortcut window", "Move to the next error") },
 
-  { "org.gnome.builder.editor-view.prev-error",
+  { "org.gnome.builder.editor-page.prev-error",
     0, NULL,
     NC_("shortcut window", "Editor shortcuts"),
     NC_("shortcut window", "Find and replace"),
@@ -78,61 +78,61 @@ static DzlShortcutEntry editor_view_shortcuts[] = {
 };
 
 void
-_ide_editor_view_init_shortcuts (IdeEditorView *self)
+_ide_editor_page_init_shortcuts (IdeEditorPage *self)
 {
   DzlShortcutController *controller;
 
-  g_return_if_fail (IDE_IS_EDITOR_VIEW (self));
+  g_return_if_fail (IDE_IS_EDITOR_PAGE (self));
 
   controller = dzl_shortcut_controller_find (GTK_WIDGET (self));
 
   dzl_shortcut_controller_add_command_action (controller,
-                                              I_("org.gnome.builder.editor-view.find"),
+                                              I_("org.gnome.builder.editor-page.find"),
                                               "<Primary>f",
                                               DZL_SHORTCUT_PHASE_BUBBLE,
-                                              I_("editor-view.find"));
+                                              I_("editor-page.find"));
 
   dzl_shortcut_controller_add_command_action (controller,
-                                              I_("org.gnome.builder.editor-view.find-replace"),
+                                              I_("org.gnome.builder.editor-page.find-replace"),
                                               "<Primary>h",
                                               DZL_SHORTCUT_PHASE_BUBBLE,
-                                              I_("editor-view.find-replace"));
+                                              I_("editor-page.find-replace"));
 
   dzl_shortcut_controller_add_command_action (controller,
-                                              I_("org.gnome.builder.editor-view.next-match"),
+                                              I_("org.gnome.builder.editor-page.next-match"),
                                               "<Primary>g",
                                               DZL_SHORTCUT_PHASE_BUBBLE,
-                                              I_("editor-view.move-next-search-result"));
+                                              I_("editor-page.move-next-search-result"));
 
   dzl_shortcut_controller_add_command_action (controller,
-                                              I_("org.gnome.builder.editor-view.prev-match"),
+                                              I_("org.gnome.builder.editor-page.prev-match"),
                                               "<Primary><Shift>g",
                                               DZL_SHORTCUT_PHASE_BUBBLE,
-                                              I_("editor-view.move-previous-search-result"));
+                                              I_("editor-page.move-previous-search-result"));
 
   dzl_shortcut_controller_add_command_action (controller,
-                                              I_("org.gnome.builder.editor-view.next-error"),
+                                              I_("org.gnome.builder.editor-page.next-error"),
                                               "<alt>n",
                                               DZL_SHORTCUT_PHASE_BUBBLE,
-                                              I_("editor-view.move-next-error"));
+                                              I_("editor-page.move-next-error"));
 
   dzl_shortcut_controller_add_command_action (controller,
-                                              I_("org.gnome.builder.editor-view.prev-error"),
+                                              I_("org.gnome.builder.editor-page.prev-error"),
                                               "<alt>p",
                                               DZL_SHORTCUT_PHASE_BUBBLE,
-                                              I_("editor-view.move-previous-error"));
+                                              I_("editor-page.move-previous-error"));
 
   dzl_shortcut_controller_add_command_action (controller,
-                                              I_("org.gnome.builder.editor-view.save"),
+                                              I_("org.gnome.builder.editor-page.save"),
                                               "<Primary>s",
                                               DZL_SHORTCUT_PHASE_BUBBLE,
-                                              I_("editor-view.save"));
+                                              I_("editor-page.save"));
 
   dzl_shortcut_controller_add_command_action (controller,
-                                              I_("org.gnome.builder.editor-view.save-as"),
+                                              I_("org.gnome.builder.editor-page.save-as"),
                                               "<Primary><Shift>s",
                                               DZL_SHORTCUT_PHASE_BUBBLE,
-                                              I_("editor-view.save-as"));
+                                              I_("editor-page.save-as"));
 
   dzl_shortcut_manager_add_shortcut_entries (NULL,
                                              editor_view_shortcuts,
diff --git a/src/libide/editor/ide-editor-view.c b/src/libide/editor/ide-editor-page.c
similarity index 67%
rename from src/libide/editor/ide-editor-view.c
rename to src/libide/editor/ide-editor-page.c
index fad280d02..e31a8b22e 100644
--- a/src/libide/editor/ide-editor-view.c
+++ b/src/libide/editor/ide-editor-page.c
@@ -1,4 +1,4 @@
-/* ide-editor-view.c
+/* ide-editor-page.c
  *
  * Copyright 2017-2019 Christian Hergert <chergert redhat com>
  *
@@ -18,20 +18,19 @@
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
 
-#define G_LOG_DOMAIN "ide-editor-view"
+#define G_LOG_DOMAIN "ide-editor-page"
 
 #include "config.h"
 
 #include <dazzle.h>
 #include <libpeas/peas.h>
+#include <gtksourceview/gtksource.h>
 #include <pango/pangofc-fontmap.h>
 
-#include "buffers/ide-buffer-private.h"
-#include "completion/ide-completion.h"
-#include "editor/ide-editor-private.h"
-#include "sourceview/ide-line-change-gutter-renderer.h"
-#include "util/ide-glib.h"
-#include "util/ide-gtk.h"
+#include "ide-editor-page.h"
+#include "ide-editor-page-addin.h"
+#include "ide-editor-private.h"
+#include "ide-line-change-gutter-renderer.h"
 
 #define AUTO_HIDE_TIMEOUT_SECONDS 5
 
@@ -45,9 +44,9 @@ enum {
   N_PROPS
 };
 
-static void ide_editor_view_update_reveal_timer (IdeEditorView *self);
+static void ide_editor_page_update_reveal_timer (IdeEditorPage *self);
 
-G_DEFINE_TYPE (IdeEditorView, ide_editor_view, IDE_TYPE_LAYOUT_VIEW)
+G_DEFINE_TYPE (IdeEditorPage, ide_editor_page, IDE_TYPE_PAGE)
 
 DZL_DEFINE_COUNTER (instances, "Editor", "N Views", "Number of editor views");
 
@@ -55,7 +54,7 @@ static GParamSpec *properties [N_PROPS];
 static FcConfig *localFontConfig;
 
 static void
-ide_editor_view_load_fonts (IdeEditorView *self)
+ide_editor_page_load_fonts (IdeEditorPage *self)
 {
   PangoFontMap *font_map;
   PangoFontDescription *font_desc;
@@ -92,17 +91,16 @@ ide_editor_view_load_fonts (IdeEditorView *self)
 }
 
 static void
-ide_editor_view_update_icon (IdeEditorView *self)
+ide_editor_page_update_icon (IdeEditorPage *self)
 {
   g_autofree gchar *name = NULL;
   g_autofree gchar *content_type = NULL;
   g_autofree gchar *sniff = NULL;
   g_autoptr(GIcon) icon = NULL;
   GtkTextIter begin, end;
-  IdeFile *file;
-  GFile *gfile;
+  GFile *file;
 
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
   g_assert (IDE_IS_BUFFER (self->buffer));
 
   /* Get first 1024 bytes to help determine content type */
@@ -113,37 +111,36 @@ ide_editor_view_update_icon (IdeEditorView *self)
 
   /* Now get basename for content type */
   file = ide_buffer_get_file (self->buffer);
-  gfile = ide_file_get_file (file);
-  name = g_file_get_basename (gfile);
+  name = g_file_get_basename (file);
 
   /* Guess content type */
   content_type = g_content_type_guess (name, (const guchar *)sniff, strlen (sniff), NULL);
 
   /* Update icon to match guess */
   icon = ide_g_content_type_get_symbolic_icon (content_type);
-  ide_layout_view_set_icon (IDE_LAYOUT_VIEW (self), icon);
+  ide_page_set_icon (IDE_PAGE (self), icon);
 }
 
 static void
-ide_editor_view_buffer_notify_failed (IdeEditorView *self,
+ide_editor_page_buffer_notify_failed (IdeEditorPage *self,
                                       GParamSpec    *pspec,
                                       IdeBuffer     *buffer)
 {
   gboolean failed;
 
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
   g_assert (IDE_IS_BUFFER (buffer));
 
   failed = ide_buffer_get_failed (buffer);
 
-  ide_layout_view_set_failed (IDE_LAYOUT_VIEW (self), failed);
+  ide_page_set_failed (IDE_PAGE (self), failed);
 }
 
 static void
-ide_editor_view_stop_search (IdeEditorView      *self,
+ide_editor_page_stop_search (IdeEditorPage      *self,
                              IdeEditorSearchBar *search_bar)
 {
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
   g_assert (IDE_IS_EDITOR_SEARCH_BAR (search_bar));
 
   gtk_revealer_set_reveal_child (self->search_revealer, FALSE);
@@ -151,11 +148,11 @@ ide_editor_view_stop_search (IdeEditorView      *self,
 }
 
 static void
-ide_editor_view_notify_child_revealed (IdeEditorView *self,
+ide_editor_page_notify_child_revealed (IdeEditorPage *self,
                                        GParamSpec    *pspec,
                                        GtkRevealer   *revealer)
 {
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
   g_assert (GTK_IS_REVEALER (revealer));
 
   if (gtk_revealer_get_child_revealed (revealer))
@@ -172,29 +169,28 @@ ide_editor_view_notify_child_revealed (IdeEditorView *self,
 }
 
 static gboolean
-ide_editor_view_focus_in_event (IdeEditorView *self,
+ide_editor_page_focus_in_event (IdeEditorPage *self,
                                 GdkEventFocus *focus,
                                 IdeSourceView *source_view)
 {
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
   g_assert (IDE_IS_SOURCE_VIEW (source_view));
 
   gtk_revealer_set_reveal_child (self->search_revealer, FALSE);
 
-  if (self->buffer != NULL)
-    ide_buffer_check_for_volume_change (self->buffer);
+  ide_page_mark_used (IDE_PAGE (self));
 
   return GDK_EVENT_PROPAGATE;
 }
 
 static void
-ide_editor_view_buffer_loaded (IdeEditorView *self,
+ide_editor_page_buffer_loaded (IdeEditorPage *self,
                                IdeBuffer     *buffer)
 {
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
   g_assert (IDE_IS_BUFFER (buffer));
 
-  ide_editor_view_update_icon (self);
+  ide_editor_page_update_icon (self);
 
   /* Scroll to the insertion location once the buffer
    * has loaded. This is useful if it is not onscreen.
@@ -203,22 +199,22 @@ ide_editor_view_buffer_loaded (IdeEditorView *self,
 }
 
 static void
-ide_editor_view_buffer_modified_changed (IdeEditorView *self,
+ide_editor_page_buffer_modified_changed (IdeEditorPage *self,
                                          IdeBuffer     *buffer)
 {
   gboolean modified = FALSE;
 
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
   g_assert (IDE_IS_BUFFER (buffer));
 
   if (!ide_buffer_get_loading (buffer))
     modified = gtk_text_buffer_get_modified (GTK_TEXT_BUFFER (buffer));
 
-  ide_layout_view_set_modified (IDE_LAYOUT_VIEW (self), modified);
+  ide_page_set_modified (IDE_PAGE (self), modified);
 }
 
 static void
-ide_editor_view_buffer_notify_language_cb (IdeExtensionSetAdapter *set,
+ide_editor_page_buffer_notify_language_cb (IdeExtensionSetAdapter *set,
                                            PeasPluginInfo         *plugin_info,
                                            PeasExtension          *exten,
                                            gpointer                user_data)
@@ -227,19 +223,19 @@ ide_editor_view_buffer_notify_language_cb (IdeExtensionSetAdapter *set,
 
   g_assert (IDE_IS_EXTENSION_SET_ADAPTER (set));
   g_assert (plugin_info != NULL);
-  g_assert (IDE_IS_EDITOR_VIEW_ADDIN (exten));
+  g_assert (IDE_IS_EDITOR_PAGE_ADDIN (exten));
 
-  ide_editor_view_addin_language_changed (IDE_EDITOR_VIEW_ADDIN (exten), language_id);
+  ide_editor_page_addin_language_changed (IDE_EDITOR_PAGE_ADDIN (exten), language_id);
 }
 
 static void
-ide_editor_view_buffer_notify_language (IdeEditorView *self,
+ide_editor_page_buffer_notify_language (IdeEditorPage *self,
                                         GParamSpec    *pspec,
                                         IdeBuffer     *buffer)
 {
   const gchar *lang_id = NULL;
 
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
   g_assert (IDE_IS_BUFFER (buffer));
 
   if (self->addins == NULL)
@@ -250,14 +246,14 @@ ide_editor_view_buffer_notify_language (IdeEditorView *self,
   /* Update extensions that change based on language */
   ide_extension_set_adapter_set_value (self->addins, lang_id);
   ide_extension_set_adapter_foreach (self->addins,
-                                     ide_editor_view_buffer_notify_language_cb,
+                                     ide_editor_page_buffer_notify_language_cb,
                                      (gpointer)lang_id);
 
-  ide_editor_view_update_icon (self);
+  ide_editor_page_update_icon (self);
 }
 
 static void
-ide_editor_view_buffer_notify_style_scheme (IdeEditorView *self,
+ide_editor_page_buffer_notify_style_scheme (IdeEditorPage *self,
                                             GParamSpec    *pspec,
                                             IdeBuffer     *buffer)
 {
@@ -269,7 +265,7 @@ ide_editor_view_buffer_notify_style_scheme (IdeEditorView *self,
   gboolean foreground_set = FALSE;
   GdkRGBA rgba;
 
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
   g_assert (IDE_IS_BUFFER (buffer));
 
   if (NULL == (scheme = gtk_source_buffer_get_style_scheme (GTK_SOURCE_BUFFER (buffer))) ||
@@ -287,28 +283,28 @@ ide_editor_view_buffer_notify_style_scheme (IdeEditorView *self,
     goto unset_primary_color;
 
   if (background_set && background != NULL && gdk_rgba_parse (&rgba, background))
-    ide_layout_view_set_primary_color_bg (IDE_LAYOUT_VIEW (self), &rgba);
+    ide_page_set_primary_color_bg (IDE_PAGE (self), &rgba);
   else
     goto unset_primary_color;
 
   if (foreground_set && foreground != NULL && gdk_rgba_parse (&rgba, foreground))
-    ide_layout_view_set_primary_color_fg (IDE_LAYOUT_VIEW (self), &rgba);
+    ide_page_set_primary_color_fg (IDE_PAGE (self), &rgba);
   else
-    ide_layout_view_set_primary_color_fg (IDE_LAYOUT_VIEW (self), NULL);
+    ide_page_set_primary_color_fg (IDE_PAGE (self), NULL);
 
   return;
 
 unset_primary_color:
-  ide_layout_view_set_primary_color_bg (IDE_LAYOUT_VIEW (self), NULL);
-  ide_layout_view_set_primary_color_fg (IDE_LAYOUT_VIEW (self), NULL);
+  ide_page_set_primary_color_bg (IDE_PAGE (self), NULL);
+  ide_page_set_primary_color_fg (IDE_PAGE (self), NULL);
 }
 
 static void
-ide_editor_view__buffer_notify_changed_on_volume (IdeEditorView *self,
+ide_editor_page__buffer_notify_changed_on_volume (IdeEditorPage *self,
                                                   GParamSpec    *pspec,
                                                   IdeBuffer     *buffer)
 {
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
   g_assert (IDE_IS_BUFFER (buffer));
 
   gtk_revealer_set_reveal_child (self->modified_revealer,
@@ -316,26 +312,26 @@ ide_editor_view__buffer_notify_changed_on_volume (IdeEditorView *self,
 }
 
 static void
-ide_editor_view_hide_reload_bar (IdeEditorView *self,
+ide_editor_page_hide_reload_bar (IdeEditorPage *self,
                                  GtkWidget     *button)
 {
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
 
   gtk_revealer_set_reveal_child (self->modified_revealer, FALSE);
 }
 
 static gboolean
-ide_editor_view_source_view_event (IdeEditorView *self,
+ide_editor_page_source_view_event (IdeEditorPage *self,
                                    GdkEvent      *event,
                                    IdeSourceView *source_view)
 {
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
   g_assert (event != NULL);
   g_assert (IDE_IS_SOURCE_VIEW (source_view) || GTK_SOURCE_IS_MAP (source_view));
 
   if (self->auto_hide_map)
     {
-      ide_editor_view_update_reveal_timer (self);
+      ide_editor_page_update_reveal_timer (self);
       gtk_revealer_set_reveal_child (self->map_revealer, TRUE);
     }
 
@@ -343,25 +339,25 @@ ide_editor_view_source_view_event (IdeEditorView *self,
 }
 
 static void
-ide_editor_view_bind_signals (IdeEditorView  *self,
+ide_editor_page_bind_signals (IdeEditorPage  *self,
                               IdeBuffer      *buffer,
                               DzlSignalGroup *buffer_signals)
 {
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
   g_assert (IDE_IS_BUFFER (buffer));
   g_assert (DZL_IS_SIGNAL_GROUP (buffer_signals));
 
-  ide_editor_view_buffer_modified_changed (self, buffer);
-  ide_editor_view_buffer_notify_language (self, NULL, buffer);
-  ide_editor_view_buffer_notify_style_scheme (self, NULL, buffer);
-  ide_editor_view_buffer_notify_failed (self, NULL, buffer);
+  ide_editor_page_buffer_modified_changed (self, buffer);
+  ide_editor_page_buffer_notify_language (self, NULL, buffer);
+  ide_editor_page_buffer_notify_style_scheme (self, NULL, buffer);
+  ide_editor_page_buffer_notify_failed (self, NULL, buffer);
 }
 
 static void
-ide_editor_view_set_buffer (IdeEditorView *self,
+ide_editor_page_set_buffer (IdeEditorPage *self,
                             IdeBuffer     *buffer)
 {
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
   g_assert (!buffer || IDE_IS_BUFFER (buffer));
 
   if (g_set_object (&self->buffer, buffer))
@@ -371,107 +367,107 @@ ide_editor_view_set_buffer (IdeEditorView *self,
       gtk_text_view_set_buffer (GTK_TEXT_VIEW (self->source_view),
                                 GTK_TEXT_BUFFER (buffer));
       gtk_drag_dest_unset (GTK_WIDGET (self->source_view));
-      ide_editor_view_update_icon (self);
+      ide_editor_page_update_icon (self);
     }
 }
 
-static IdeLayoutView *
-ide_editor_view_create_split_view (IdeLayoutView *view)
+static IdePage *
+ide_editor_page_create_split (IdePage *view)
 {
-  IdeEditorView *self = (IdeEditorView *)view;
+  IdeEditorPage *self = (IdeEditorPage *)view;
 
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
 
-  return g_object_new (IDE_TYPE_EDITOR_VIEW,
+  return g_object_new (IDE_TYPE_EDITOR_PAGE,
                        "buffer", self->buffer,
                        "visible", TRUE,
                        NULL);
 }
 
 static void
-ide_editor_view_notify_stack_set (IdeExtensionSetAdapter *set,
+ide_editor_page_notify_frame_set (IdeExtensionSetAdapter *set,
                                   PeasPluginInfo         *plugin_info,
                                   PeasExtension          *exten,
                                   gpointer                user_data)
 {
-  IdeLayoutStack *stack = user_data;
-  IdeEditorViewAddin *addin = (IdeEditorViewAddin *)exten;
+  IdeFrame *frame = user_data;
+  IdeEditorPageAddin *addin = (IdeEditorPageAddin *)exten;
 
   g_assert (IDE_IS_EXTENSION_SET_ADAPTER (set));
   g_assert (plugin_info != NULL);
-  g_assert (IDE_IS_EDITOR_VIEW_ADDIN (addin));
-  g_assert (IDE_IS_LAYOUT_STACK (stack));
+  g_assert (IDE_IS_EDITOR_PAGE_ADDIN (addin));
+  g_assert (IDE_IS_FRAME (frame));
 
-  ide_editor_view_addin_stack_set (addin, stack);
+  ide_editor_page_addin_frame_set (addin, frame);
 }
 
 static void
-ide_editor_view_addin_added (IdeExtensionSetAdapter *set,
+ide_editor_page_addin_added (IdeExtensionSetAdapter *set,
                              PeasPluginInfo         *plugin_info,
                              PeasExtension          *exten,
                              gpointer                user_data)
 {
-  IdeEditorView *self = user_data;
-  IdeEditorViewAddin *addin = (IdeEditorViewAddin *)exten;
+  IdeEditorPage *self = user_data;
+  IdeEditorPageAddin *addin = (IdeEditorPageAddin *)exten;
 
   g_assert (IDE_IS_EXTENSION_SET_ADAPTER (set));
   g_assert (plugin_info != NULL);
-  g_assert (IDE_IS_EDITOR_VIEW_ADDIN (addin));
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE_ADDIN (addin));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
 
-  ide_editor_view_addin_load (addin, self);
+  ide_editor_page_addin_load (addin, self);
 
   /*
-   * Notify of the current stack, but refetch the stack pointer just
+   * Notify of the current frame, but refetch the frame pointer just
    * to be sure we aren't re-using an old pointer in case we're racing
    * with a finalizer.
    */
-  if (self->last_stack_ptr != NULL)
+  if (self->last_frame_ptr != NULL)
     {
-      GtkWidget *stack = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_LAYOUT_STACK);
-      if (stack != NULL)
-        ide_editor_view_addin_stack_set (addin, IDE_LAYOUT_STACK (stack));
+      GtkWidget *frame = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_FRAME);
+      if (frame != NULL)
+        ide_editor_page_addin_frame_set (addin, IDE_FRAME (frame));
     }
 }
 
 static void
-ide_editor_view_addin_removed (IdeExtensionSetAdapter *set,
+ide_editor_page_addin_removed (IdeExtensionSetAdapter *set,
                                PeasPluginInfo         *plugin_info,
                                PeasExtension          *exten,
                                gpointer                user_data)
 {
-  IdeEditorView *self = user_data;
-  IdeEditorViewAddin *addin = (IdeEditorViewAddin *)exten;
+  IdeEditorPage *self = user_data;
+  IdeEditorPageAddin *addin = (IdeEditorPageAddin *)exten;
 
   g_assert (IDE_IS_EXTENSION_SET_ADAPTER (set));
   g_assert (plugin_info != NULL);
-  g_assert (IDE_IS_EDITOR_VIEW_ADDIN (addin));
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE_ADDIN (addin));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
 
-  ide_editor_view_addin_unload (addin, self);
+  ide_editor_page_addin_unload (addin, self);
 }
 
 static void
-ide_editor_view_hierarchy_changed (GtkWidget *widget,
+ide_editor_page_hierarchy_changed (GtkWidget *widget,
                                    GtkWidget *old_toplevel)
 {
-  IdeEditorView *self = (IdeEditorView *)widget;
-  IdeLayoutStack *stack;
+  IdeEditorPage *self = (IdeEditorPage *)widget;
+  IdeFrame *frame;
   IdeContext *context;
 
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
   g_assert (!old_toplevel || GTK_IS_WIDGET (old_toplevel));
 
   /*
-   * We don't need to chain up today, but if IdeLayoutView starts
+   * We don't need to chain up today, but if IdePage starts
    * using the hierarchy_changed signal to handle anything, we want
    * to make sure we aren't surprised.
    */
-  if (GTK_WIDGET_CLASS (ide_editor_view_parent_class)->hierarchy_changed)
-    GTK_WIDGET_CLASS (ide_editor_view_parent_class)->hierarchy_changed (widget, old_toplevel);
+  if (GTK_WIDGET_CLASS (ide_editor_page_parent_class)->hierarchy_changed)
+    GTK_WIDGET_CLASS (ide_editor_page_parent_class)->hierarchy_changed (widget, old_toplevel);
 
   context = ide_widget_get_context (GTK_WIDGET (self));
-  stack = (IdeLayoutStack *)gtk_widget_get_ancestor (widget, IDE_TYPE_LAYOUT_STACK);
+  frame = (IdeFrame *)gtk_widget_get_ancestor (widget, IDE_TYPE_FRAME);
 
   /*
    * We don't want to create addins until the widget has been placed into
@@ -480,46 +476,46 @@ ide_editor_view_hierarchy_changed (GtkWidget *widget,
    */
   if (context != NULL && self->addins == NULL)
     {
-      self->addins = ide_extension_set_adapter_new (context,
+      self->addins = ide_extension_set_adapter_new (IDE_OBJECT (context),
                                                     peas_engine_get_default (),
-                                                    IDE_TYPE_EDITOR_VIEW_ADDIN,
-                                                    "Editor-View-Languages",
-                                                    ide_editor_view_get_language_id (self));
+                                                    IDE_TYPE_EDITOR_PAGE_ADDIN,
+                                                    "Editor-Page-Languages",
+                                                    ide_editor_page_get_language_id (self));
 
       g_signal_connect (self->addins,
                         "extension-added",
-                        G_CALLBACK (ide_editor_view_addin_added),
+                        G_CALLBACK (ide_editor_page_addin_added),
                         self);
 
       g_signal_connect (self->addins,
                         "extension-removed",
-                        G_CALLBACK (ide_editor_view_addin_removed),
+                        G_CALLBACK (ide_editor_page_addin_removed),
                         self);
 
       ide_extension_set_adapter_foreach (self->addins,
-                                         ide_editor_view_addin_added,
+                                         ide_editor_page_addin_added,
                                          self);
     }
 
   /*
-   * If we have been moved into a new stack, notify the addins of the
+   * If we have been moved into a new frame, notify the addins of the
    * hierarchy change.
    */
-  if (stack != NULL && stack != self->last_stack_ptr && self->addins != NULL)
+  if (frame != NULL && frame != self->last_frame_ptr && self->addins != NULL)
     {
-      self->last_stack_ptr = stack;
+      self->last_frame_ptr = frame;
       ide_extension_set_adapter_foreach (self->addins,
-                                         ide_editor_view_notify_stack_set,
-                                         stack);
+                                         ide_editor_page_notify_frame_set,
+                                         frame);
     }
 }
 
 static void
-ide_editor_view_update_map (IdeEditorView *self)
+ide_editor_page_update_map (IdeEditorPage *self)
 {
   GtkWidget *parent;
 
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
 
   parent = gtk_widget_get_parent (GTK_WIDGET (self->map));
 
@@ -536,19 +532,19 @@ ide_editor_view_update_map (IdeEditorView *self)
   gtk_widget_set_visible (GTK_WIDGET (self->map), self->show_map);
   gtk_revealer_set_reveal_child (self->map_revealer, self->show_map);
 
-  ide_editor_view_update_reveal_timer (self);
+  ide_editor_page_update_reveal_timer (self);
 
   g_object_unref (self->map);
 }
 
 static void
-search_revealer_notify_reveal_child (IdeEditorView *self,
+search_revealer_notify_reveal_child (IdeEditorPage *self,
                                      GParamSpec    *pspec,
                                      GtkRevealer   *revealer)
 {
   IdeCompletion *completion;
 
-  g_return_if_fail (IDE_IS_EDITOR_VIEW (self));
+  g_return_if_fail (IDE_IS_EDITOR_PAGE (self));
   g_return_if_fail (pspec != NULL);
   g_return_if_fail (GTK_IS_REVEALER (revealer));
 
@@ -567,7 +563,7 @@ search_revealer_notify_reveal_child (IdeEditorView *self,
 
       /*
        * Block the completion while the search bar is set. It only
-       * slows things down like search/replace functionality. We'll
+       * slows things down like replace functionality. We'll
        * restore it above when we clear state.
        */
       ide_completion_block_interactive (completion);
@@ -575,28 +571,25 @@ search_revealer_notify_reveal_child (IdeEditorView *self,
 }
 
 static void
-ide_editor_view_focus_location (IdeEditorView     *self,
-                                IdeSourceLocation *location,
-                                IdeSourceView     *source_view)
+ide_editor_page_focus_location (IdeEditorPage *self,
+                                IdeLocation   *location,
+                                IdeSourceView *source_view)
 {
-  IdeWorkbench *workbench;
-  IdePerspective *editor;
+  GtkWidget *editor;
 
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
   g_assert (location != NULL);
   g_assert (IDE_IS_SOURCE_VIEW (source_view));
 
-  workbench = ide_widget_get_workbench (GTK_WIDGET (self));
-  editor = ide_workbench_get_perspective_by_name (workbench, "editor");
-
-  ide_editor_perspective_focus_location (IDE_EDITOR_PERSPECTIVE (editor), location);
+  editor = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_EDITOR_SURFACE);
+  ide_editor_surface_focus_location (IDE_EDITOR_SURFACE (editor), location);
 }
 
 static void
-ide_editor_view_clear_search (IdeEditorView *self,
+ide_editor_page_clear_search (IdeEditorPage *self,
                               IdeSourceView *view)
 {
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
   g_assert (IDE_IS_EDITOR_SEARCH (self->search));
   g_assert (IDE_IS_SOURCE_VIEW (view));
 
@@ -605,7 +598,7 @@ ide_editor_view_clear_search (IdeEditorView *self,
 }
 
 static void
-ide_editor_view_move_search (IdeEditorView    *self,
+ide_editor_page_move_search (IdeEditorPage    *self,
                              GtkDirectionType  dir,
                              gboolean          extend_selection,
                              gboolean          select_match,
@@ -616,7 +609,7 @@ ide_editor_view_move_search (IdeEditorView    *self,
 {
   IdeEditorSearchSelect sel = 0;
 
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
   g_assert (IDE_IS_EDITOR_SEARCH (self->search));
   g_assert (IDE_IS_SOURCE_VIEW (view));
 
@@ -670,7 +663,7 @@ ide_editor_view_move_search (IdeEditorView    *self,
 }
 
 static void
-ide_editor_view_set_search_text (IdeEditorView *self,
+ide_editor_page_set_search_text (IdeEditorPage *self,
                                  const gchar   *search_text,
                                  gboolean       from_selection,
                                  IdeSourceView *view)
@@ -679,7 +672,7 @@ ide_editor_view_set_search_text (IdeEditorView *self,
   GtkTextIter begin;
   GtkTextIter end;
 
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
   g_assert (IDE_IS_EDITOR_SEARCH (self->search));
   g_assert (search_text != NULL || from_selection);
   g_assert (IDE_IS_SOURCE_VIEW (view));
@@ -706,66 +699,65 @@ ide_editor_view_set_search_text (IdeEditorView *self,
 }
 
 static void
-ide_editor_view_constructed (GObject *object)
+ide_editor_page_constructed (GObject *object)
 {
-  IdeEditorView *self = (IdeEditorView *)object;
+  IdeEditorPage *self = (IdeEditorPage *)object;
   GtkSourceGutterRenderer *renderer;
   GtkSourceGutter *gutter;
 
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
 
-  G_OBJECT_CLASS (ide_editor_view_parent_class)->constructed (object);
+  G_OBJECT_CLASS (ide_editor_page_parent_class)->constructed (object);
 
   gutter = gtk_source_view_get_gutter (GTK_SOURCE_VIEW (self->map), GTK_TEXT_WINDOW_LEFT);
   renderer = g_object_new (IDE_TYPE_LINE_CHANGE_GUTTER_RENDERER,
-                           "show-line-deletions", TRUE,
                            "size", 1,
                            "visible", TRUE,
                            NULL);
   gtk_source_gutter_insert (gutter, renderer, 0);
 
-  _ide_editor_view_init_actions (self);
-  _ide_editor_view_init_shortcuts (self);
-  _ide_editor_view_init_settings (self);
+  _ide_editor_page_init_actions (self);
+  _ide_editor_page_init_shortcuts (self);
+  _ide_editor_page_init_settings (self);
 
   g_signal_connect_swapped (self->source_view,
                             "focus-in-event",
-                            G_CALLBACK (ide_editor_view_focus_in_event),
+                            G_CALLBACK (ide_editor_page_focus_in_event),
                             self);
 
   g_signal_connect_swapped (self->source_view,
                             "motion-notify-event",
-                            G_CALLBACK (ide_editor_view_source_view_event),
+                            G_CALLBACK (ide_editor_page_source_view_event),
                             self);
 
   g_signal_connect_swapped (self->source_view,
                             "scroll-event",
-                            G_CALLBACK (ide_editor_view_source_view_event),
+                            G_CALLBACK (ide_editor_page_source_view_event),
                             self);
 
   g_signal_connect_swapped (self->source_view,
                             "focus-location",
-                            G_CALLBACK (ide_editor_view_focus_location),
+                            G_CALLBACK (ide_editor_page_focus_location),
                             self);
 
   g_signal_connect_swapped (self->source_view,
                             "set-search-text",
-                            G_CALLBACK (ide_editor_view_set_search_text),
+                            G_CALLBACK (ide_editor_page_set_search_text),
                             self);
 
   g_signal_connect_swapped (self->source_view,
                             "clear-search",
-                            G_CALLBACK (ide_editor_view_clear_search),
+                            G_CALLBACK (ide_editor_page_clear_search),
                             self);
 
   g_signal_connect_swapped (self->source_view,
                             "move-search",
-                            G_CALLBACK (ide_editor_view_move_search),
+                            G_CALLBACK (ide_editor_page_move_search),
                             self);
 
   g_signal_connect_swapped (self->map,
                             "motion-notify-event",
-                            G_CALLBACK (ide_editor_view_source_view_event),
+                            G_CALLBACK (ide_editor_page_source_view_event),
                             self);
 
 
@@ -785,16 +777,16 @@ ide_editor_view_constructed (GObject *object)
   gtk_widget_insert_action_group (GTK_WIDGET (self), "editor-search",
                                   G_ACTION_GROUP (self->search));
 
-  ide_editor_view_load_fonts (self);
-  ide_editor_view_update_map (self);
+  ide_editor_page_load_fonts (self);
+  ide_editor_page_update_map (self);
 }
 
 static void
-ide_editor_view_destroy (GtkWidget *widget)
+ide_editor_page_destroy (GtkWidget *widget)
 {
-  IdeEditorView *self = (IdeEditorView *)widget;
+  IdeEditorPage *self = (IdeEditorPage *)widget;
 
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
 
   /*
    * WORKAROUND: We need to reset the drag dest to avoid warnings by Gtk
@@ -807,10 +799,10 @@ ide_editor_view_destroy (GtkWidget *widget)
 
   dzl_clear_source (&self->toggle_map_source);
 
-  g_clear_object (&self->addins);
+  ide_clear_and_destroy_object (&self->addins);
 
   gtk_widget_insert_action_group (widget, "editor-search", NULL);
-  gtk_widget_insert_action_group (widget, "editor-view", NULL);
+  gtk_widget_insert_action_group (widget, "editor-page", NULL);
 
   g_cancellable_cancel (self->destroy_cancellable);
   g_clear_object (&self->destroy_cancellable);
@@ -833,45 +825,45 @@ ide_editor_view_destroy (GtkWidget *widget)
       g_clear_object (&self->buffer_signals);
     }
 
-  GTK_WIDGET_CLASS (ide_editor_view_parent_class)->destroy (widget);
+  GTK_WIDGET_CLASS (ide_editor_page_parent_class)->destroy (widget);
 }
 
 static void
-ide_editor_view_finalize (GObject *object)
+ide_editor_page_finalize (GObject *object)
 {
-  G_OBJECT_CLASS (ide_editor_view_parent_class)->finalize (object);
+  G_OBJECT_CLASS (ide_editor_page_parent_class)->finalize (object);
 
   DZL_COUNTER_DEC (instances);
 }
 
 static void
-ide_editor_view_get_property (GObject    *object,
+ide_editor_page_get_property (GObject    *object,
                               guint       prop_id,
                               GValue     *value,
                               GParamSpec *pspec)
 {
-  IdeEditorView *self = IDE_EDITOR_VIEW (object);
+  IdeEditorPage *self = IDE_EDITOR_PAGE (object);
 
   switch (prop_id)
     {
     case PROP_AUTO_HIDE_MAP:
-      g_value_set_boolean (value, ide_editor_view_get_auto_hide_map (self));
+      g_value_set_boolean (value, ide_editor_page_get_auto_hide_map (self));
       break;
 
     case PROP_BUFFER:
-      g_value_set_object (value, ide_editor_view_get_buffer (self));
+      g_value_set_object (value, ide_editor_page_get_buffer (self));
       break;
 
     case PROP_VIEW:
-      g_value_set_object (value, ide_editor_view_get_view (self));
+      g_value_set_object (value, ide_editor_page_get_view (self));
       break;
 
     case PROP_SEARCH:
-      g_value_set_object (value, ide_editor_view_get_search (self));
+      g_value_set_object (value, ide_editor_page_get_search (self));
       break;
 
     case PROP_SHOW_MAP:
-      g_value_set_boolean (value, ide_editor_view_get_show_map (self));
+      g_value_set_boolean (value, ide_editor_page_get_show_map (self));
       break;
 
     default:
@@ -880,25 +872,25 @@ ide_editor_view_get_property (GObject    *object,
 }
 
 static void
-ide_editor_view_set_property (GObject      *object,
+ide_editor_page_set_property (GObject      *object,
                               guint         prop_id,
                               const GValue *value,
                               GParamSpec   *pspec)
 {
-  IdeEditorView *self = IDE_EDITOR_VIEW (object);
+  IdeEditorPage *self = IDE_EDITOR_PAGE (object);
 
   switch (prop_id)
     {
     case PROP_AUTO_HIDE_MAP:
-      ide_editor_view_set_auto_hide_map (self, g_value_get_boolean (value));
+      ide_editor_page_set_auto_hide_map (self, g_value_get_boolean (value));
       break;
 
     case PROP_BUFFER:
-      ide_editor_view_set_buffer (self, g_value_get_object (value));
+      ide_editor_page_set_buffer (self, g_value_get_object (value));
       break;
 
     case PROP_SHOW_MAP:
-      ide_editor_view_set_show_map (self, g_value_get_boolean (value));
+      ide_editor_page_set_show_map (self, g_value_get_boolean (value));
       break;
 
     default:
@@ -907,21 +899,21 @@ ide_editor_view_set_property (GObject      *object,
 }
 
 static void
-ide_editor_view_class_init (IdeEditorViewClass *klass)
+ide_editor_page_class_init (IdeEditorPageClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
-  IdeLayoutViewClass *layout_view_class = IDE_LAYOUT_VIEW_CLASS (klass);
+  IdePageClass *page_class = IDE_PAGE_CLASS (klass);
 
-  object_class->finalize = ide_editor_view_finalize;
-  object_class->constructed = ide_editor_view_constructed;
-  object_class->get_property = ide_editor_view_get_property;
-  object_class->set_property = ide_editor_view_set_property;
+  object_class->finalize = ide_editor_page_finalize;
+  object_class->constructed = ide_editor_page_constructed;
+  object_class->get_property = ide_editor_page_get_property;
+  object_class->set_property = ide_editor_page_set_property;
 
-  widget_class->destroy = ide_editor_view_destroy;
-  widget_class->hierarchy_changed = ide_editor_view_hierarchy_changed;
+  widget_class->destroy = ide_editor_page_destroy;
+  widget_class->hierarchy_changed = ide_editor_page_hierarchy_changed;
 
-  layout_view_class->create_split_view = ide_editor_view_create_split_view;
+  page_class->create_split = ide_editor_page_create_split;
 
   properties [PROP_BUFFER] =
     g_param_spec_object ("buffer",
@@ -960,34 +952,34 @@ ide_editor_view_class_init (IdeEditorViewClass *klass)
 
   g_object_class_install_properties (object_class, N_PROPS, properties);
 
-  gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/builder/ui/ide-editor-view.ui");
-  gtk_widget_class_bind_template_child (widget_class, IdeEditorView, map);
-  gtk_widget_class_bind_template_child (widget_class, IdeEditorView, map_revealer);
-  gtk_widget_class_bind_template_child (widget_class, IdeEditorView, overlay);
-  gtk_widget_class_bind_template_child (widget_class, IdeEditorView, progress_bar);
-  gtk_widget_class_bind_template_child (widget_class, IdeEditorView, scroller);
-  gtk_widget_class_bind_template_child (widget_class, IdeEditorView, scroller_box);
-  gtk_widget_class_bind_template_child (widget_class, IdeEditorView, search_bar);
-  gtk_widget_class_bind_template_child (widget_class, IdeEditorView, search_revealer);
-  gtk_widget_class_bind_template_child (widget_class, IdeEditorView, modified_revealer);
-  gtk_widget_class_bind_template_child (widget_class, IdeEditorView, modified_cancel_button);
-  gtk_widget_class_bind_template_child (widget_class, IdeEditorView, source_view);
-  gtk_widget_class_bind_template_callback (widget_class, ide_editor_view_notify_child_revealed);
-  gtk_widget_class_bind_template_callback (widget_class, ide_editor_view_stop_search);
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/libide-editor/ui/ide-editor-page.ui");
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorPage, map);
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorPage, map_revealer);
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorPage, overlay);
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorPage, progress_bar);
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorPage, scroller);
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorPage, scroller_box);
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorPage, search_bar);
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorPage, search_revealer);
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorPage, modified_revealer);
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorPage, modified_cancel_button);
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorPage, source_view);
+  gtk_widget_class_bind_template_callback (widget_class, ide_editor_page_notify_child_revealed);
+  gtk_widget_class_bind_template_callback (widget_class, ide_editor_page_stop_search);
 
   g_type_ensure (IDE_TYPE_SOURCE_VIEW);
   g_type_ensure (IDE_TYPE_EDITOR_SEARCH_BAR);
 }
 
 static void
-ide_editor_view_init (IdeEditorView *self)
+ide_editor_page_init (IdeEditorPage *self)
 {
   DZL_COUNTER_INC (instances);
 
   gtk_widget_init_template (GTK_WIDGET (self));
 
-  ide_layout_view_set_can_split (IDE_LAYOUT_VIEW (self), TRUE);
-  ide_layout_view_set_menu_id (IDE_LAYOUT_VIEW (self), "ide-editor-view-document-menu");
+  ide_page_set_can_split (IDE_PAGE (self), TRUE);
+  ide_page_set_menu_id (IDE_PAGE (self), "ide-editor-page-document-menu");
 
   self->destroy_cancellable = g_cancellable_new ();
 
@@ -996,41 +988,41 @@ ide_editor_view_init (IdeEditorView *self)
 
   dzl_signal_group_connect_swapped (self->buffer_signals,
                                     "loaded",
-                                    G_CALLBACK (ide_editor_view_buffer_loaded),
+                                    G_CALLBACK (ide_editor_page_buffer_loaded),
                                     self);
 
   dzl_signal_group_connect_swapped (self->buffer_signals,
                                     "modified-changed",
-                                    G_CALLBACK (ide_editor_view_buffer_modified_changed),
+                                    G_CALLBACK (ide_editor_page_buffer_modified_changed),
                                     self);
 
   dzl_signal_group_connect_swapped (self->buffer_signals,
                                     "notify::failed",
-                                    G_CALLBACK (ide_editor_view_buffer_notify_failed),
+                                    G_CALLBACK (ide_editor_page_buffer_notify_failed),
                                     self);
 
   dzl_signal_group_connect_swapped (self->buffer_signals,
                                     "notify::language",
-                                    G_CALLBACK (ide_editor_view_buffer_notify_language),
+                                    G_CALLBACK (ide_editor_page_buffer_notify_language),
                                     self);
 
   dzl_signal_group_connect_swapped (self->buffer_signals,
                                     "notify::style-scheme",
-                                    G_CALLBACK (ide_editor_view_buffer_notify_style_scheme),
+                                    G_CALLBACK (ide_editor_page_buffer_notify_style_scheme),
                                     self);
   dzl_signal_group_connect_swapped (self->buffer_signals,
                                     "notify::changed-on-volume",
-                                    G_CALLBACK (ide_editor_view__buffer_notify_changed_on_volume),
+                                    G_CALLBACK (ide_editor_page__buffer_notify_changed_on_volume),
                                     self);
 
   g_signal_connect_swapped (self->buffer_signals,
                             "bind",
-                            G_CALLBACK (ide_editor_view_bind_signals),
+                            G_CALLBACK (ide_editor_page_bind_signals),
                             self);
 
   g_signal_connect_object (self->modified_cancel_button,
                            "clicked",
-                           G_CALLBACK (ide_editor_view_hide_reload_bar),
+                           G_CALLBACK (ide_editor_page_hide_reload_bar),
                            self,
                            G_CONNECT_SWAPPED);
 
@@ -1043,8 +1035,8 @@ ide_editor_view_init (IdeEditorView *self)
 }
 
 /**
- * ide_editor_view_get_buffer:
- * @self: a #IdeEditorView
+ * ide_editor_page_get_buffer:
+ * @self: a #IdeEditorPage
  *
  * Gets the underlying buffer for the view.
  *
@@ -1053,34 +1045,34 @@ ide_editor_view_init (IdeEditorView *self)
  * Since: 3.32
  */
 IdeBuffer *
-ide_editor_view_get_buffer (IdeEditorView *self)
+ide_editor_page_get_buffer (IdeEditorPage *self)
 {
-  g_return_val_if_fail (IDE_IS_EDITOR_VIEW (self), NULL);
+  g_return_val_if_fail (IDE_IS_EDITOR_PAGE (self), NULL);
 
   return self->buffer;
 }
 
 /**
- * ide_editor_view_get_view:
- * @self: a #IdeEditorView
+ * ide_editor_page_get_view:
+ * @self: a #IdeEditorPage
  *
- * Gets the #IdeSourceView that is part of the #IdeEditorView.
+ * Gets the #IdeSourceView that is part of the #IdeEditorPage.
  *
  * Returns: (transfer none): An #IdeSourceView
  *
  * Since: 3.32
  */
 IdeSourceView *
-ide_editor_view_get_view (IdeEditorView *self)
+ide_editor_page_get_view (IdeEditorPage *self)
 {
-  g_return_val_if_fail (IDE_IS_EDITOR_VIEW (self), NULL);
+  g_return_val_if_fail (IDE_IS_EDITOR_PAGE (self), NULL);
 
   return self->source_view;
 }
 
 /**
- * ide_editor_view_get_language_id:
- * @self: a #IdeEditorView
+ * ide_editor_page_get_language_id:
+ * @self: a #IdeEditorPage
  *
  * This is a helper to get the language-id of the underlying buffer.
  *
@@ -1089,9 +1081,9 @@ ide_editor_view_get_view (IdeEditorView *self)
  * Since: 3.32
  */
 const gchar *
-ide_editor_view_get_language_id (IdeEditorView *self)
+ide_editor_page_get_language_id (IdeEditorPage *self)
 {
-  g_return_val_if_fail (IDE_IS_EDITOR_VIEW (self), NULL);
+  g_return_val_if_fail (IDE_IS_EDITOR_PAGE (self), NULL);
 
   if (self->buffer != NULL)
     {
@@ -1107,8 +1099,8 @@ ide_editor_view_get_language_id (IdeEditorView *self)
 }
 
 /**
- * ide_editor_view_scroll_to_line:
- * @self: a #IdeEditorView
+ * ide_editor_page_scroll_to_line:
+ * @self: a #IdeEditorPage
  * @line: the line to scroll to
  *
  * This is a helper to quickly jump to a given line without all the frills. It
@@ -1123,19 +1115,19 @@ ide_editor_view_get_language_id (IdeEditorView *self)
  * Since: 3.32
  */
 void
-ide_editor_view_scroll_to_line (IdeEditorView *self,
+ide_editor_page_scroll_to_line (IdeEditorPage *self,
                                 guint          line)
 {
-  ide_editor_view_scroll_to_line_offset (self, line, 0);
+  ide_editor_page_scroll_to_line_offset (self, line, 0);
 }
 
 /**
- * ide_editor_view_scroll_to_line_offset:
- * @self: a #IdeEditorView
+ * ide_editor_page_scroll_to_line_offset:
+ * @self: a #IdeEditorPage
  * @line: the line to scroll to
  * @line_offset: the line offset
  *
- * Like ide_editor_view_scroll_to_line() but allows specifying the
+ * Like ide_editor_page_scroll_to_line() but allows specifying the
  * line offset (column) to place the cursor on.
  *
  * This will move the insert cursor.
@@ -1148,13 +1140,13 @@ ide_editor_view_scroll_to_line (IdeEditorView *self,
  * Since: 3.32
  */
 void
-ide_editor_view_scroll_to_line_offset (IdeEditorView *self,
+ide_editor_page_scroll_to_line_offset (IdeEditorPage *self,
                                        guint          line,
                                        guint          line_offset)
 {
   GtkTextIter iter;
 
-  g_return_if_fail (IDE_IS_EDITOR_VIEW (self));
+  g_return_if_fail (IDE_IS_EDITOR_PAGE (self));
   g_return_if_fail (self->buffer != NULL);
   g_return_if_fail (line <= G_MAXINT);
 
@@ -1178,19 +1170,19 @@ ide_editor_view_scroll_to_line_offset (IdeEditorView *self,
 }
 
 gboolean
-ide_editor_view_get_auto_hide_map (IdeEditorView *self)
+ide_editor_page_get_auto_hide_map (IdeEditorPage *self)
 {
-  g_return_val_if_fail (IDE_IS_EDITOR_VIEW (self), FALSE);
+  g_return_val_if_fail (IDE_IS_EDITOR_PAGE (self), FALSE);
 
   return self->auto_hide_map;
 }
 
 static gboolean
-ide_editor_view_auto_hide_cb (gpointer user_data)
+ide_editor_page_auto_hide_cb (gpointer user_data)
 {
-  IdeEditorView *self = user_data;
+  IdeEditorPage *self = user_data;
 
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
 
   self->toggle_map_source = 0;
   gtk_revealer_set_reveal_child (self->map_revealer, FALSE);
@@ -1199,9 +1191,9 @@ ide_editor_view_auto_hide_cb (gpointer user_data)
 }
 
 static void
-ide_editor_view_update_reveal_timer (IdeEditorView *self)
+ide_editor_page_update_reveal_timer (IdeEditorPage *self)
 {
-  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self));
 
   dzl_clear_source (&self->toggle_map_source);
 
@@ -1210,41 +1202,41 @@ ide_editor_view_update_reveal_timer (IdeEditorView *self)
       self->toggle_map_source =
         gdk_threads_add_timeout_seconds_full (G_PRIORITY_LOW,
                                               AUTO_HIDE_TIMEOUT_SECONDS,
-                                              ide_editor_view_auto_hide_cb,
+                                              ide_editor_page_auto_hide_cb,
                                               g_object_ref (self),
                                               g_object_unref);
     }
 }
 
 void
-ide_editor_view_set_auto_hide_map (IdeEditorView *self,
+ide_editor_page_set_auto_hide_map (IdeEditorPage *self,
                                    gboolean       auto_hide_map)
 {
-  g_return_if_fail (IDE_IS_EDITOR_VIEW (self));
+  g_return_if_fail (IDE_IS_EDITOR_PAGE (self));
 
   auto_hide_map = !!auto_hide_map;
 
   if (auto_hide_map != self->auto_hide_map)
     {
       self->auto_hide_map = auto_hide_map;
-      ide_editor_view_update_map (self);
+      ide_editor_page_update_map (self);
       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_AUTO_HIDE_MAP]);
     }
 }
 
 gboolean
-ide_editor_view_get_show_map (IdeEditorView *self)
+ide_editor_page_get_show_map (IdeEditorPage *self)
 {
-  g_return_val_if_fail (IDE_IS_EDITOR_VIEW (self), FALSE);
+  g_return_val_if_fail (IDE_IS_EDITOR_PAGE (self), FALSE);
 
   return self->show_map;
 }
 
 void
-ide_editor_view_set_show_map (IdeEditorView *self,
+ide_editor_page_set_show_map (IdeEditorPage *self,
                               gboolean       show_map)
 {
-  g_return_if_fail (IDE_IS_EDITOR_VIEW (self));
+  g_return_if_fail (IDE_IS_EDITOR_PAGE (self));
 
   show_map = !!show_map;
 
@@ -1254,14 +1246,14 @@ ide_editor_view_set_show_map (IdeEditorView *self,
       g_object_set (self->scroller,
                     "vscrollbar-policy", show_map ? GTK_POLICY_EXTERNAL : GTK_POLICY_AUTOMATIC,
                     NULL);
-      ide_editor_view_update_map (self);
+      ide_editor_page_update_map (self);
       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_AUTO_HIDE_MAP]);
     }
 }
 
 /**
- * ide_editor_view_set_language:
- * @self: a #IdeEditorView
+ * ide_editor_page_set_language:
+ * @self: a #IdeEditorPage
  *
  * This is a convenience function to set the language on the underlying
  * #IdeBuffer text buffer.
@@ -1269,18 +1261,18 @@ ide_editor_view_set_show_map (IdeEditorView *self,
  * Since: 3.32
  */
 void
-ide_editor_view_set_language (IdeEditorView     *self,
+ide_editor_page_set_language (IdeEditorPage     *self,
                               GtkSourceLanguage *language)
 {
-  g_return_if_fail (IDE_IS_EDITOR_VIEW (self));
+  g_return_if_fail (IDE_IS_EDITOR_PAGE (self));
   g_return_if_fail (!language || GTK_SOURCE_IS_LANGUAGE (language));
 
   gtk_source_buffer_set_language (GTK_SOURCE_BUFFER (self->buffer), language);
 }
 
 /**
- * ide_editor_view_get_language:
- * @self: a #IdeEditorView
+ * ide_editor_page_get_language:
+ * @self: a #IdeEditorPage
  *
  * Gets the #GtkSourceLanguage that is used by the underlying buffer.
  *
@@ -1289,16 +1281,16 @@ ide_editor_view_set_language (IdeEditorView     *self,
  * Since: 3.32
  */
 GtkSourceLanguage *
-ide_editor_view_get_language (IdeEditorView *self)
+ide_editor_page_get_language (IdeEditorPage *self)
 {
-  g_return_val_if_fail (IDE_IS_EDITOR_VIEW (self), NULL);
+  g_return_val_if_fail (IDE_IS_EDITOR_PAGE (self), NULL);
 
   return gtk_source_buffer_get_language (GTK_SOURCE_BUFFER (self->buffer));
 }
 
 /**
- * ide_editor_view_move_next_error:
- * @self: a #IdeEditorView
+ * ide_editor_page_move_next_error:
+ * @self: a #IdeEditorPage
  *
  * Moves to the next error, if any.
  *
@@ -1307,16 +1299,16 @@ ide_editor_view_get_language (IdeEditorView *self)
  * Since: 3.32
  */
 void
-ide_editor_view_move_next_error (IdeEditorView *self)
+ide_editor_page_move_next_error (IdeEditorPage *self)
 {
-  g_return_if_fail (IDE_IS_EDITOR_VIEW (self));
+  g_return_if_fail (IDE_IS_EDITOR_PAGE (self));
 
   g_signal_emit_by_name (self->source_view, "move-error", GTK_DIR_DOWN);
 }
 
 /**
- * ide_editor_view_move_previous_error:
- * @self: a #IdeEditorView
+ * ide_editor_page_move_previous_error:
+ * @self: a #IdeEditorPage
  *
  * Moves the insertion cursor to the previous error.
  *
@@ -1325,16 +1317,16 @@ ide_editor_view_move_next_error (IdeEditorView *self)
  * Since: 3.32
  */
 void
-ide_editor_view_move_previous_error (IdeEditorView *self)
+ide_editor_page_move_previous_error (IdeEditorPage *self)
 {
-  g_return_if_fail (IDE_IS_EDITOR_VIEW (self));
+  g_return_if_fail (IDE_IS_EDITOR_PAGE (self));
 
   g_signal_emit_by_name (self->source_view, "move-error", GTK_DIR_UP);
 }
 
 /**
- * ide_editor_view_move_next_search_result:
- * @self: a #IdeEditorView
+ * ide_editor_page_move_next_search_result:
+ * @self: a #IdeEditorPage
  *
  * Moves the insertion cursor to the next search result.
  *
@@ -1343,9 +1335,9 @@ ide_editor_view_move_previous_error (IdeEditorView *self)
  * Since: 3.32
  */
 void
-ide_editor_view_move_next_search_result (IdeEditorView *self)
+ide_editor_page_move_next_search_result (IdeEditorPage *self)
 {
-  g_return_if_fail (IDE_IS_EDITOR_VIEW (self));
+  g_return_if_fail (IDE_IS_EDITOR_PAGE (self));
   g_return_if_fail (self->destroy_cancellable != NULL);
   g_return_if_fail (self->buffer != NULL);
 
@@ -1353,8 +1345,8 @@ ide_editor_view_move_next_search_result (IdeEditorView *self)
 }
 
 /**
- * ide_editor_view_move_previous_search_result:
- * @self: a #IdeEditorView
+ * ide_editor_page_move_previous_search_result:
+ * @self: a #IdeEditorPage
  *
  * Moves the insertion cursor to the previous search result.
  *
@@ -1363,9 +1355,9 @@ ide_editor_view_move_next_search_result (IdeEditorView *self)
  * Since: 3.32
  */
 void
-ide_editor_view_move_previous_search_result (IdeEditorView *self)
+ide_editor_page_move_previous_search_result (IdeEditorPage *self)
 {
-  g_return_if_fail (IDE_IS_EDITOR_VIEW (self));
+  g_return_if_fail (IDE_IS_EDITOR_PAGE (self));
   g_return_if_fail (self->destroy_cancellable != NULL);
   g_return_if_fail (self->buffer != NULL);
 
@@ -1373,8 +1365,8 @@ ide_editor_view_move_previous_search_result (IdeEditorView *self)
 }
 
 /**
- * ide_editor_view_get_search:
- * @self: a #IdeEditorView
+ * ide_editor_page_get_search:
+ * @self: a #IdeEditorPage
  *
  * Gets the #IdeEditorSearch used to search within the document.
  *
@@ -1383,9 +1375,33 @@ ide_editor_view_move_previous_search_result (IdeEditorView *self)
  * Since: 3.32
  */
 IdeEditorSearch *
-ide_editor_view_get_search (IdeEditorView *self)
+ide_editor_page_get_search (IdeEditorPage *self)
 {
-  g_return_val_if_fail (IDE_IS_EDITOR_VIEW (self), NULL);
+  g_return_val_if_fail (IDE_IS_EDITOR_PAGE (self), NULL);
 
   return self->search;
 }
+
+/**
+ * ide_editor_page_get_file:
+ * @self: a #IdeEditorPage
+ *
+ * Gets the #GFile that represents the current file. This may be a temporary
+ * file, but a #GFile will still be used for the temporary file.
+ *
+ * Returns: (transfer none): a #GFile for the current buffer
+ *
+ * Since: 3.32
+ */
+GFile *
+ide_editor_page_get_file (IdeEditorPage *self)
+{
+  IdeBuffer *buffer;
+
+  g_return_val_if_fail (IDE_IS_EDITOR_PAGE (self), NULL);
+
+  if ((buffer = ide_editor_page_get_buffer (self)))
+    return ide_buffer_get_file (buffer);
+
+  return NULL;
+}
diff --git a/src/libide/editor/ide-editor-page.h b/src/libide/editor/ide-editor-page.h
new file mode 100644
index 000000000..e47c9bfd9
--- /dev/null
+++ b/src/libide/editor/ide-editor-page.h
@@ -0,0 +1,82 @@
+/* ide-editor-page.h
+ *
+ * Copyright 2017-2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_EDITOR_INSIDE) && !defined (IDE_EDITOR_COMPILATION)
+# error "Only <libide-editor.h> can be included directly."
+#endif
+
+#include <libide-code.h>
+#include <libide-core.h>
+#include <libide-gui.h>
+#include <libide-sourceview.h>
+
+#include "ide-editor-search.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_EDITOR_PAGE (ide_editor_page_get_type())
+
+IDE_AVAILABLE_IN_3_32
+G_DECLARE_FINAL_TYPE (IdeEditorPage, ide_editor_page, IDE, EDITOR_PAGE, IdePage)
+
+IDE_AVAILABLE_IN_3_32
+GFile             *ide_editor_page_get_file                    (IdeEditorPage     *self);
+IDE_AVAILABLE_IN_3_32
+IdeBuffer         *ide_editor_page_get_buffer                  (IdeEditorPage     *self);
+IDE_AVAILABLE_IN_3_32
+IdeSourceView     *ide_editor_page_get_view                    (IdeEditorPage     *self);
+IDE_AVAILABLE_IN_3_32
+IdeEditorSearch   *ide_editor_page_get_search                  (IdeEditorPage     *self);
+IDE_AVAILABLE_IN_3_32
+const gchar       *ide_editor_page_get_language_id             (IdeEditorPage     *self);
+IDE_AVAILABLE_IN_3_32
+void               ide_editor_page_scroll_to_line              (IdeEditorPage     *self,
+                                                                guint              line);
+IDE_AVAILABLE_IN_3_32
+void               ide_editor_page_scroll_to_line_offset       (IdeEditorPage     *self,
+                                                                guint              line,
+                                                                guint              line_offset);
+IDE_AVAILABLE_IN_3_32
+gboolean           ide_editor_page_get_auto_hide_map           (IdeEditorPage     *self);
+IDE_AVAILABLE_IN_3_32
+void               ide_editor_page_set_auto_hide_map           (IdeEditorPage     *self,
+                                                                gboolean           auto_hide_map);
+IDE_AVAILABLE_IN_3_32
+gboolean           ide_editor_page_get_show_map                (IdeEditorPage     *self);
+IDE_AVAILABLE_IN_3_32
+void               ide_editor_page_set_show_map                (IdeEditorPage     *self,
+                                                                gboolean           show_map);
+IDE_AVAILABLE_IN_3_32
+GtkSourceLanguage *ide_editor_page_get_language                (IdeEditorPage     *self);
+IDE_AVAILABLE_IN_3_32
+void               ide_editor_page_set_language                (IdeEditorPage     *self,
+                                                                GtkSourceLanguage *language);
+IDE_AVAILABLE_IN_3_32
+void               ide_editor_page_move_next_error             (IdeEditorPage     *self);
+IDE_AVAILABLE_IN_3_32
+void               ide_editor_page_move_previous_error         (IdeEditorPage     *self);
+IDE_AVAILABLE_IN_3_32
+void               ide_editor_page_move_next_search_result     (IdeEditorPage     *self);
+IDE_AVAILABLE_IN_3_32
+void               ide_editor_page_move_previous_search_result (IdeEditorPage     *self);
+
+G_END_DECLS
diff --git a/src/libide/editor/ide-editor-view.ui b/src/libide/editor/ide-editor-page.ui
similarity index 95%
rename from src/libide/editor/ide-editor-view.ui
rename to src/libide/editor/ide-editor-page.ui
index 369625aff..f62c387c3 100644
--- a/src/libide/editor/ide-editor-view.ui
+++ b/src/libide/editor/ide-editor-page.ui
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <template class="IdeEditorView" parent="IdeLayoutView">
+  <template class="IdeEditorPage" parent="IdePage">
     <child>
       <object class="GtkOverlay" id="overlay">
         <property name="visible">true</property>
@@ -12,11 +12,11 @@
             <property name="margin-right">12</property>
             <property name="reveal-child">false</property>
             <property name="visible">true</property>
-            <signal name="notify::child-revealed" handler="ide_editor_view_notify_child_revealed" 
swapped="true" object="IdeEditorView"/>
+            <signal name="notify::child-revealed" handler="ide_editor_page_notify_child_revealed" 
swapped="true" object="IdeEditorPage"/>
             <child>
               <object class="IdeEditorSearchBar" id="search_bar">
                 <property name="visible">true</property>
-                <signal name="stop-search" handler="ide_editor_view_stop_search" swapped="true" 
object="IdeEditorView"/>
+                <signal name="stop-search" handler="ide_editor_page_stop_search" swapped="true" 
object="IdeEditorPage"/>
               </object>
             </child>
           </object>
diff --git a/src/libide/editor/ide-editor-plugin-private.h b/src/libide/editor/ide-editor-plugin-private.h
new file mode 100644
index 000000000..c86344740
--- /dev/null
+++ b/src/libide/editor/ide-editor-plugin-private.h
@@ -0,0 +1,27 @@
+/* ide-editor-plugin-private.h
+ *
+ * Copyright 2018-2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libide-core.h>
+#include <libpeas/peas.h>
+
+IDE_AVAILABLE_IN_3_32
+void _ide_editor_register_types (PeasObjectModule *module);
diff --git a/src/libide/editor/ide-editor-print-operation.c b/src/libide/editor/ide-editor-print-operation.c
index a6edcb6ae..dbe895b3b 100644
--- a/src/libide/editor/ide-editor-print-operation.c
+++ b/src/libide/editor/ide-editor-print-operation.c
@@ -25,8 +25,8 @@
 #include <glib/gi18n.h>
 #include <gtksourceview/gtksource.h>
 
-#include "editor/ide-editor-print-operation.h"
-#include "editor/ide-editor-view.h"
+#include "ide-editor-print-operation.h"
+#include "ide-editor-page.h"
 
 struct _IdeEditorPrintOperation
 {
diff --git a/src/libide/editor/ide-editor-print-operation.h b/src/libide/editor/ide-editor-print-operation.h
index 0fc0bcdee..3e95e8388 100644
--- a/src/libide/editor/ide-editor-print-operation.h
+++ b/src/libide/editor/ide-editor-print-operation.h
@@ -20,7 +20,7 @@
 
 #pragma once
 
-#include "editor/ide-editor-view.h"
+#include "ide-editor-page.h"
 
 G_BEGIN_DECLS
 
@@ -28,6 +28,6 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (IdeEditorPrintOperation, ide_editor_print_operation, IDE, EDITOR_PRINT_OPERATION, 
GtkPrintOperation)
 
-IdeEditorPrintOperation  *ide_editor_print_operation_new    (IdeSourceView *view);
+IdeEditorPrintOperation *ide_editor_print_operation_new (IdeSourceView *view);
 
 G_END_DECLS
diff --git a/src/libide/editor/ide-editor-private.h b/src/libide/editor/ide-editor-private.h
index 94b70e4ab..1a3a3996e 100644
--- a/src/libide/editor/ide-editor-private.h
+++ b/src/libide/editor/ide-editor-private.h
@@ -1,6 +1,6 @@
 /* ide-editor-private.h
  *
- * Copyright 2017-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2018-2019 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
@@ -20,32 +20,29 @@
 
 #pragma once
 
-#include <dazzle.h>
+#include <libide-gui.h>
+#include <libide-plugins.h>
+#include <libide-sourceview.h>
 #include <libpeas/peas.h>
 
-#include "editor/ide-editor-perspective.h"
-#include "editor/ide-editor-properties.h"
-#include "editor/ide-editor-search.h"
-#include "editor/ide-editor-search-bar.h"
-#include "editor/ide-editor-sidebar.h"
-#include "editor/ide-editor-view-addin.h"
-#include "editor/ide-editor-view.h"
-#include "layout/ide-layout-grid.h"
-#include "layout/ide-layout-view.h"
-#include "plugins/ide-extension-set-adapter.h"
+#include "ide-editor-addin.h"
+#include "ide-editor-page.h"
+#include "ide-editor-search-bar.h"
+#include "ide-editor-search.h"
+#include "ide-editor-sidebar.h"
+#include "ide-editor-surface.h"
 
 G_BEGIN_DECLS
 
-struct _IdeEditorPerspective
+struct _IdeEditorSurface
 {
-  IdeLayout            parent_instance;
+  IdeSurface           parent_instance;
 
   PeasExtensionSet    *addins;
 
   /* Template widgets */
-  IdeLayoutGrid       *grid;
+  IdeGrid             *grid;
   GtkOverlay          *overlay;
-  IdeEditorProperties *properties;
   GtkStack            *loading_stack;
 
   /* State before entering focus mode */
@@ -53,9 +50,9 @@ struct _IdeEditorPerspective
   guint                prefocus_had_bottom : 1;
 };
 
-struct _IdeEditorView
+struct _IdeEditorPage
 {
-  IdeLayoutView            parent_instance;
+  IdePage                  parent_instance;
 
   IdeExtensionSetAdapter  *addins;
 
@@ -82,8 +79,8 @@ struct _IdeEditorView
   GtkRevealer             *modified_revealer;
   GtkButton               *modified_cancel_button;
 
-  /* Raw pointer used to determine when stack changes */
-  IdeLayoutStack          *last_stack_ptr;
+  /* Raw pointer used to determine when frame changes */
+  IdeFrame                *last_frame_ptr;
 
   guint                    toggle_map_source;
 
@@ -91,18 +88,16 @@ struct _IdeEditorView
   guint                    show_map : 1;
 };
 
-void _ide_editor_view_init_actions           (IdeEditorView        *self);
-void _ide_editor_view_init_settings          (IdeEditorView        *self);
-void _ide_editor_view_init_shortcuts         (IdeEditorView        *self);
-void _ide_editor_view_update_actions         (IdeEditorView        *self);
-void _ide_editor_search_bar_init_shortcuts   (IdeEditorSearchBar   *self);
-void _ide_editor_sidebar_set_open_pages      (IdeEditorSidebar     *self,
-                                              GListModel           *open_pages);
-void _ide_editor_perspective_show_properties (IdeEditorPerspective *self,
-                                              IdeEditorView        *view);
-void _ide_editor_perspective_set_loading     (IdeEditorPerspective *self,
-                                              gboolean              loading);
-void _ide_editor_perspective_init_actions    (IdeEditorPerspective *self);
-void _ide_editor_perspective_init_shortcuts  (IdeEditorPerspective *self);
+void _ide_editor_page_init_actions         (IdeEditorPage      *self);
+void _ide_editor_page_init_settings        (IdeEditorPage      *self);
+void _ide_editor_page_init_shortcuts       (IdeEditorPage      *self);
+void _ide_editor_page_update_actions       (IdeEditorPage      *self);
+void _ide_editor_search_bar_init_shortcuts (IdeEditorSearchBar *self);
+void _ide_editor_sidebar_set_open_pages    (IdeEditorSidebar   *self,
+                                            GListModel         *open_pages);
+void _ide_editor_surface_set_loading       (IdeEditorSurface   *self,
+                                            gboolean            loading);
+void _ide_editor_surface_init_actions      (IdeEditorSurface   *self);
+void _ide_editor_surface_init_shortcuts    (IdeEditorSurface   *self);
 
 G_END_DECLS
diff --git a/src/libide/editor/ide-editor-search-bar-shortcuts.c 
b/src/libide/editor/ide-editor-search-bar-shortcuts.c
index 14b742250..af5e5a197 100644
--- a/src/libide/editor/ide-editor-search-bar-shortcuts.c
+++ b/src/libide/editor/ide-editor-search-bar-shortcuts.c
@@ -22,8 +22,8 @@
 
 #include "config.h"
 
-#include "editor/ide-editor-private.h"
-#include "editor/ide-editor-search-bar.h"
+#include "ide-editor-private.h"
+#include "ide-editor-search-bar.h"
 
 static void
 ide_editor_search_bar_shortcuts_activate_previous (GtkWidget *widget,
diff --git a/src/libide/editor/ide-editor-search-bar.c b/src/libide/editor/ide-editor-search-bar.c
index e9e3a9238..dbabceba3 100644
--- a/src/libide/editor/ide-editor-search-bar.c
+++ b/src/libide/editor/ide-editor-search-bar.c
@@ -25,10 +25,9 @@
 #include <dazzle.h>
 #include <glib/gi18n.h>
 
-#include "editor/ide-editor-private.h"
-#include "editor/ide-editor-search.h"
-#include "editor/ide-editor-search-bar.h"
-#include "search/ide-tagged-entry.h"
+#include "ide-editor-private.h"
+#include "ide-editor-search.h"
+#include "ide-editor-search-bar.h"
 
 struct _IdeEditorSearchBar
 {
@@ -447,7 +446,7 @@ ide_editor_search_bar_class_init (IdeEditorSearchBarClass *klass)
                                 g_cclosure_marshal_VOID__VOID,
                                 G_TYPE_NONE, 0);
 
-  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/builder/ui/ide-editor-search-bar.ui");
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/libide-editor/ui/ide-editor-search-bar.ui");
   gtk_widget_class_bind_template_child (widget_class, IdeEditorSearchBar, case_sensitive);
   gtk_widget_class_bind_template_child (widget_class, IdeEditorSearchBar, replace_all_button);
   gtk_widget_class_bind_template_child (widget_class, IdeEditorSearchBar, replace_button);
diff --git a/src/libide/editor/ide-editor-search-bar.h b/src/libide/editor/ide-editor-search-bar.h
index a28cfd5ed..034b36381 100644
--- a/src/libide/editor/ide-editor-search-bar.h
+++ b/src/libide/editor/ide-editor-search-bar.h
@@ -23,7 +23,7 @@
 #include <dazzle.h>
 #include <gtksourceview/gtksource.h>
 
-#include "editor/ide-editor-search.h"
+#include "ide-editor-search.h"
 
 G_BEGIN_DECLS
 
diff --git a/src/libide/editor/ide-editor-search.c b/src/libide/editor/ide-editor-search.c
index 49c4ac9fa..d621fa981 100644
--- a/src/libide/editor/ide-editor-search.c
+++ b/src/libide/editor/ide-editor-search.c
@@ -23,10 +23,10 @@
 #include "config.h"
 
 #include <dazzle.h>
+#include <libide-sourceview.h>
 #include <string.h>
 
-#include "editor/ide-editor-search.h"
-#include "sourceview/ide-source-view.h"
+#include "ide-editor-search.h"
 
 /**
  * SECTION:ide-editor-search
@@ -96,20 +96,23 @@ enum {
   N_PROPS
 };
 
-static void ide_editor_search_actions_move_next     (IdeEditorSearch *self,
-                                                     GVariant        *param);
-static void ide_editor_search_actions_move_previous (IdeEditorSearch *self,
-                                                     GVariant        *param);
-static void ide_editor_search_actions_replace       (IdeEditorSearch *self,
-                                                     GVariant        *param);
-static void ide_editor_search_actions_replace_all   (IdeEditorSearch *self,
-                                                     GVariant        *param);
+static void ide_editor_search_actions_move_next        (IdeEditorSearch *self,
+                                                        GVariant        *param);
+static void ide_editor_search_actions_move_previous    (IdeEditorSearch *self,
+                                                        GVariant        *param);
+static void ide_editor_search_actions_replace          (IdeEditorSearch *self,
+                                                        GVariant        *param);
+static void ide_editor_search_actions_replace_all      (IdeEditorSearch *self,
+                                                        GVariant        *param);
+static void ide_editor_search_actions_at_word_boundary (IdeEditorSearch *self,
+                                                        GVariant        *param);
 
 DZL_DEFINE_ACTION_GROUP (IdeEditorSearch, ide_editor_search, {
   { "move-next", ide_editor_search_actions_move_next },
   { "move-previous", ide_editor_search_actions_move_previous },
   { "replace", ide_editor_search_actions_replace },
   { "replace-all", ide_editor_search_actions_replace_all },
+  { "at-word-boundaries", ide_editor_search_actions_at_word_boundary, "b" },
 })
 
 G_DEFINE_TYPE_WITH_CODE (IdeEditorSearch, ide_editor_search, G_TYPE_OBJECT,
@@ -1410,7 +1413,7 @@ ide_editor_search_backward_cb (GObject      *object,
   g_assert (GTK_SOURCE_IS_SEARCH_CONTEXT (context));
   g_assert (IDE_IS_EDITOR_SEARCH (self));
 
-  if (gtk_source_search_context_backward_finish (context, result, &begin, &end, NULL, NULL))
+  if (gtk_source_search_context_forward_finish (context, result, &begin, &end, NULL, NULL))
     {
       if (self->view != NULL)
         {
@@ -1914,6 +1917,13 @@ ide_editor_search_actions_move_previous (IdeEditorSearch *self,
   ide_editor_search_move (self, IDE_EDITOR_SEARCH_PREVIOUS);
 }
 
+static void
+ide_editor_search_actions_at_word_boundary (IdeEditorSearch *self,
+                                            GVariant        *param)
+{
+  ide_editor_search_set_at_word_boundaries (self, g_variant_get_boolean (param));
+}
+
 static void
 ide_editor_search_actions_replace_all (IdeEditorSearch *self,
                                        GVariant        *param)
diff --git a/src/libide/editor/ide-editor-search.h b/src/libide/editor/ide-editor-search.h
index 8fd0d93d2..12b9122d4 100644
--- a/src/libide/editor/ide-editor-search.h
+++ b/src/libide/editor/ide-editor-search.h
@@ -20,9 +20,12 @@
 
 #pragma once
 
-#include <gtksourceview/gtksource.h>
+#if !defined (IDE_EDITOR_INSIDE) && !defined (IDE_EDITOR_COMPILATION)
+# error "Only <libide-editor.h> can be included directly."
+#endif
 
-#include "ide-version-macros.h"
+#include <gtksourceview/gtksource.h>
+#include <libide-core.h>
 
 G_BEGIN_DECLS
 
@@ -44,6 +47,8 @@ typedef enum
  *
  * This enum can be used to determine how the selection should be extending
  * when moving between the search results.
+ *
+ * Since: 3.32
  */
 typedef enum
 {
diff --git a/src/libide/editor/ide-editor-settings-dialog.c b/src/libide/editor/ide-editor-settings-dialog.c
new file mode 100644
index 000000000..b1b1d67ca
--- /dev/null
+++ b/src/libide/editor/ide-editor-settings-dialog.c
@@ -0,0 +1,331 @@
+/* ide-editor-settings-dialog.c
+ *
+ * Copyright 2018 Christian Hergert <unknown domain org>
+ *
+ * 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-editor-settings"
+
+#include "config.h"
+
+#include "ide-editor-settings-dialog.h"
+
+struct _IdeEditorSettingsDialog
+{
+  GtkDialog       parent_instance;
+
+  IdeEditorPage  *page;
+
+  GtkTreeView    *tree_view;
+  GtkListStore   *store;
+  GtkSearchEntry *entry;
+};
+
+G_DEFINE_TYPE (IdeEditorSettingsDialog, ide_editor_settings_dialog, GTK_TYPE_DIALOG)
+
+enum {
+  PROP_0,
+  PROP_PAGE,
+  N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+ide_editor_settings_dialog_row_activated (IdeEditorSettingsDialog *self,
+                                          GtkTreePath             *path,
+                                          GtkTreeViewColumn       *column,
+                                          GtkTreeView             *tree_view)
+{
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_EDITOR_SETTINGS_DIALOG (self));
+  g_assert (path != NULL);
+  g_assert (GTK_IS_TREE_VIEW_COLUMN (column));
+  g_assert (GTK_IS_TREE_VIEW (tree_view));
+
+  model = gtk_tree_view_get_model (tree_view);
+
+  if (gtk_tree_model_get_iter (model, &iter, path))
+    {
+      g_autofree gchar *id = NULL;
+      IdeBuffer *buffer;
+
+      gtk_tree_model_get (model, &iter, 0, &id, -1);
+
+      if ((buffer = ide_editor_page_get_buffer (self->page)))
+        ide_buffer_set_language_id (buffer, id);
+    }
+}
+
+static void
+ide_editor_settings_dialog_notify_file_settings (IdeEditorSettingsDialog *self,
+                                                 GParamSpec              *pspec,
+                                                 IdeBuffer               *buffer)
+{
+  g_return_if_fail (IDE_IS_MAIN_THREAD ());
+  g_return_if_fail (IDE_IS_EDITOR_SETTINGS_DIALOG (self));
+  g_return_if_fail (IDE_IS_BUFFER (buffer));
+
+  /* Update muxed action groups for new file-settings */
+  dzl_gtk_widget_mux_action_groups (GTK_WIDGET (self),
+                                    GTK_WIDGET (self->page),
+                                    "IDE_EDITOR_PAGE_ACTIONS");
+}
+
+static void
+ide_editor_settings_dialog_notify_language (IdeEditorSettingsDialog *self,
+                                            GParamSpec              *pspec,
+                                            IdeBuffer               *buffer)
+{
+  const gchar *lang_id;
+
+  g_return_if_fail (IDE_IS_MAIN_THREAD ());
+  g_return_if_fail (IDE_IS_EDITOR_SETTINGS_DIALOG (self));
+  g_return_if_fail (IDE_IS_BUFFER (buffer));
+
+  if ((lang_id = ide_buffer_get_language_id (buffer)))
+    {
+      GtkTreeSelection *selection = gtk_tree_view_get_selection (self->tree_view);
+      GtkTreeModel *model = gtk_tree_view_get_model (self->tree_view);
+      GtkTreeIter iter;
+
+      if (gtk_tree_model_get_iter_first (model, &iter))
+        {
+          do
+            {
+              GValue idval = {0};
+
+              gtk_tree_model_get_value (model, &iter, 0, &idval);
+
+              if (ide_str_equal0 (lang_id, g_value_get_string (&idval)))
+                {
+                  g_autoptr(GtkTreePath) path = gtk_tree_model_get_path (model, &iter);
+
+                  gtk_tree_selection_select_iter (selection, &iter);
+                  gtk_tree_view_scroll_to_cell (self->tree_view, path, NULL, FALSE, 0, 0);
+
+                  return;
+                }
+            }
+          while (gtk_tree_model_iter_next (model, &iter));
+
+          gtk_tree_selection_unselect_all (selection);
+        }
+    }
+}
+
+static gboolean
+filter_func (GtkTreeModel *model,
+             GtkTreeIter  *iter,
+             gpointer      data)
+{
+  DzlPatternSpec *spec = data;
+  GValue idval = {0};
+  GValue nameval = {0};
+  gboolean ret;
+
+  gtk_tree_model_get_value (model, iter, 0, &idval);
+  gtk_tree_model_get_value (model, iter, 1, &nameval);
+
+  ret = dzl_pattern_spec_match (spec, g_value_get_string (&idval)) ||
+        dzl_pattern_spec_match (spec, g_value_get_string (&nameval));
+
+  g_value_unset (&idval);
+  g_value_unset (&nameval);
+
+  return ret;
+}
+
+static void
+ide_editor_settings_dialog_entry_changed (IdeEditorSettingsDialog *self,
+                                          GtkSearchEntry          *entry)
+{
+  g_autoptr(GtkTreeModel) filter = NULL;
+  const gchar *text;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_EDITOR_SETTINGS_DIALOG (self));
+  g_assert (GTK_IS_SEARCH_ENTRY (entry));
+
+  text = gtk_entry_get_text (GTK_ENTRY (entry));
+  filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (self->store), NULL);
+  gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter),
+                                          filter_func,
+                                          dzl_pattern_spec_new (text),
+                                          (GDestroyNotify)dzl_pattern_spec_unref);
+
+  gtk_tree_view_set_model (self->tree_view, GTK_TREE_MODEL (filter));
+}
+
+static void
+ide_editor_settings_dialog_set_page (IdeEditorSettingsDialog *self,
+                                     IdeEditorPage           *page)
+{
+  IdeBuffer *buffer;
+
+  g_return_if_fail (IDE_IS_MAIN_THREAD ());
+  g_return_if_fail (IDE_IS_EDITOR_SETTINGS_DIALOG (self));
+
+  g_set_object (&self->page, page);
+
+  g_signal_connect_object (self->entry,
+                           "changed",
+                           G_CALLBACK (ide_editor_settings_dialog_entry_changed),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  dzl_gtk_widget_mux_action_groups (GTK_WIDGET (self),
+                                    GTK_WIDGET (page),
+                                    "IDE_EDITOR_PAGE_ACTIONS");
+
+  buffer = ide_editor_page_get_buffer (page);
+
+  g_signal_connect_object (buffer,
+                           "notify::language",
+                           G_CALLBACK (ide_editor_settings_dialog_notify_language),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  g_signal_connect_object (buffer,
+                           "notify::file-settings",
+                           G_CALLBACK (ide_editor_settings_dialog_notify_file_settings),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  ide_editor_settings_dialog_notify_language (self, NULL, buffer);
+}
+
+IdeEditorSettingsDialog *
+ide_editor_settings_dialog_new (IdeEditorPage *page)
+{
+  GtkWidget *toplevel;
+
+  g_return_val_if_fail (IDE_IS_EDITOR_PAGE (page), NULL);
+
+  if ((toplevel = gtk_widget_get_toplevel (GTK_WIDGET (page))) && !IDE_IS_WORKSPACE (toplevel))
+    toplevel = NULL;
+
+  return g_object_new (IDE_TYPE_EDITOR_SETTINGS_DIALOG,
+                       "transient-for", toplevel,
+                       "modal", FALSE,
+                       "page", page,
+                       NULL);
+}
+
+static void
+ide_editor_settings_dialog_destroy (GtkWidget *widget)
+{
+  IdeEditorSettingsDialog *self = (IdeEditorSettingsDialog *)widget;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_EDITOR_SETTINGS_DIALOG (self));
+
+  g_clear_object (&self->page);
+
+  GTK_WIDGET_CLASS (ide_editor_settings_dialog_parent_class)->destroy (widget);
+}
+
+static void
+ide_editor_settings_dialog_set_property (GObject      *object,
+                                         guint         prop_id,
+                                         const GValue *value,
+                                         GParamSpec   *pspec)
+{
+  IdeEditorSettingsDialog *self = IDE_EDITOR_SETTINGS_DIALOG (object);
+
+  switch (prop_id)
+    {
+    case PROP_PAGE:
+      ide_editor_settings_dialog_set_page (self, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_editor_settings_dialog_class_init (IdeEditorSettingsDialogClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->set_property = ide_editor_settings_dialog_set_property;
+
+  widget_class->destroy = ide_editor_settings_dialog_destroy;
+
+  properties [PROP_PAGE] =
+    g_param_spec_object ("page",
+                         "Page",
+                         "The editor page to be observed",
+                         IDE_TYPE_EDITOR_PAGE,
+                         (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | 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/libide-editor/ui/ide-editor-settings-dialog.ui");
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorSettingsDialog, entry);
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorSettingsDialog, tree_view);
+}
+
+static void
+ide_editor_settings_dialog_init (IdeEditorSettingsDialog *self)
+{
+  g_autoptr(GtkListStore) store = NULL;
+  GtkSourceLanguageManager *manager;
+  const gchar * const *lang_ids;
+  GValue idval = {0};
+  GValue nameval = {0};
+
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  gtk_window_set_resizable (GTK_WINDOW (self), FALSE);
+
+  g_signal_connect_object (self->tree_view,
+                           "row-activated",
+                           G_CALLBACK (ide_editor_settings_dialog_row_activated),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  manager = gtk_source_language_manager_get_default ();
+  lang_ids = gtk_source_language_manager_get_language_ids (manager);
+  self->store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+
+  g_value_init (&idval, G_TYPE_STRING);
+  g_value_init (&nameval, G_TYPE_STRING);
+
+  for (guint i = 0; lang_ids[i]; i++)
+    {
+      GtkSourceLanguage *lang = gtk_source_language_manager_get_language (manager, lang_ids[i]);
+      GtkTreeIter iter;
+
+      g_value_set_static_string (&idval, g_intern_string (gtk_source_language_get_id (lang)));
+      g_value_set_static_string (&nameval, g_intern_string (gtk_source_language_get_name (lang)));
+
+      gtk_list_store_append (self->store, &iter);
+      gtk_list_store_set_value (self->store, &iter, 0, &idval);
+      gtk_list_store_set_value (self->store, &iter, 1, &nameval);
+
+      g_value_reset (&idval);
+      g_value_reset (&nameval);
+    }
+
+  gtk_tree_view_set_model (self->tree_view, GTK_TREE_MODEL (self->store));
+}
diff --git a/src/libide/editor/ide-editor-settings-dialog.h b/src/libide/editor/ide-editor-settings-dialog.h
new file mode 100644
index 000000000..433f30d2c
--- /dev/null
+++ b/src/libide/editor/ide-editor-settings-dialog.h
@@ -0,0 +1,34 @@
+/* ide-editor-settings-dialog.h
+ *
+ * Copyright 2018 Christian Hergert <unknown domain org>
+ *
+ * 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+#include <libide-editor.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_EDITOR_SETTINGS_DIALOG (ide_editor_settings_dialog_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeEditorSettingsDialog, ide_editor_settings_dialog, IDE, EDITOR_SETTINGS_DIALOG, 
GtkDialog)
+
+IdeEditorSettingsDialog *ide_editor_settings_dialog_new (IdeEditorPage *page);
+
+G_END_DECLS
diff --git a/src/libide/editor/ide-editor-settings-dialog.ui b/src/libide/editor/ide-editor-settings-dialog.ui
new file mode 100644
index 000000000..cae1e6362
--- /dev/null
+++ b/src/libide/editor/ide-editor-settings-dialog.ui
@@ -0,0 +1,288 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.0 -->
+<interface>
+  <requires lib="gtk+" version="3.24"/>
+  <template class="IdeEditorSettingsDialog" parent="GtkDialog">
+    <property name="title" translatable="yes">Document Properties</property>
+    <child internal-child="vbox">
+      <object class="GtkBox">
+        <child>
+          <object class="GtkBox">
+            <property name="margin">24</property>
+            <property name="spacing">24</property>
+            <property name="visible">true</property>
+            <property name="orientation">horizontal</property>
+            <child>
+              <object class="GtkBox">
+                <property name="spacing">12</property>
+                <property name="orientation">vertical</property>
+                <property name="visible">true</property>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="label" translatable="yes">Highlight Mode</property>
+                    <property name="xalign">0.0</property>
+                    <property name="visible">true</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkSearchEntry" id="entry">
+                    <property name="width-chars">25</property>
+                    <property name="visible">true</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkScrolledWindow">
+                    <property name="hscrollbar-policy">never</property>
+                    <property name="shadow-type">in</property>
+                    <property name="vexpand">true</property>
+                    <property name="visible">true</property>
+                    <child>
+                      <object class="GtkTreeView" id="tree_view">
+                        <property name="activate-on-single-click">true</property>
+                        <property name="headers-visible">false</property>
+                        <property name="visible">true</property>
+                        <child internal-child="selection">
+                          <object class="GtkTreeSelection">
+                            <property name="mode">browse</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkTreeViewColumn">
+                             <property name="visible">true</property>
+                             <child>
+                               <object class="GtkCellRendererText">
+                                 <property name="xalign">0.0</property>
+                                 <property name="ypad">3</property>
+                                 <property name="xpad">6</property>
+                               </object>
+                               <attributes>
+                                 <attribute name="text">1</attribute>
+                               </attributes>
+                             </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkBox">
+                <property name="orientation">vertical</property>
+                <property name="spacing">12</property>
+                <property name="visible">true</property>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="label" translatable="yes">General</property>
+                    <property name="xalign">0.0</property>
+                    <property name="visible">true</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkBox">
+                    <property name="orientation">vertical</property>
+                    <property name="spacing">6</property>
+                    <property name="visible">true</property>
+                    <child>
+                      <object class="GtkCheckButton">
+                        <property name="label" translatable="yes">Display line numbers</property>
+                        <property name="action-name">source-view.show-line-numbers</property>
+                        <property name="visible">true</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkCheckButton">
+                        <property name="label" translatable="yes">Display right margin</property>
+                        <property name="action-name">file-settings.show-right-margin</property>
+                        <property name="visible">true</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkCheckButton">
+                        <property name="label" translatable="yes">Highlight current line</property>
+                        <property name="action-name">source-view.highlight-current-line</property>
+                        <property name="visible">true</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkCheckButton">
+                        <property name="label" translatable="yes">Automatic indentation</property>
+                        <property name="action-name">file-settings.auto-indent</property>
+                        <property name="visible">true</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkCheckButton">
+                        <property name="label" translatable="yes">Smart backspace</property>
+                        <property name="action-name">source-view.smart-backspace</property>
+                        <property name="tooltip-text" translatable="yes">Enabling smart backspace will treat 
multiple spaces as a tabs</property>
+                        <property name="visible">true</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkCheckButton">
+                        <property name="visible">true</property>
+                        <property name="action-name">file-settings.insert-trailing-newline</property>
+                        <property name="label" translatable="yes">Insert trailing newline</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkCheckButton">
+                        <property name="visible">true</property>
+                        <property name="action-name">file-settings.overwrite-braces</property>
+                        <property name="label" translatable="yes">Overwrite trailing braces and 
quotations</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkGrid">
+                    <property name="column-spacing">12</property>
+                    <property name="row-spacing">12</property>
+                    <property name="visible">true</property>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="label" translatable="yes">Indentation</property>
+                        <property name="visible">true</property>
+                        <property name="valign">baseline</property>
+                        <property name="xalign">0.0</property>
+                        <attributes>
+                          <attribute name="weight" value="bold"/>
+                        </attributes>
+                      </object>
+                      <packing>
+                        <property name="top-attach">0</property>
+                        <property name="left-attach">0</property>
+                        <property name="width">1</property>
+                        <property name="height">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkBox">
+                        <property name="margin-top">6</property>
+                        <property name="visible">true</property>
+                        <property name="orientation">horizontal</property>
+                        <property name="hexpand">true</property>
+                        <style>
+                          <class name="linked"/>
+                        </style>
+                        <child>
+                          <object class="GtkToggleButton">
+                            <property name="visible">true</property>
+                            <property name="label" translatable="yes">2</property>
+                            <property name="focus-on-click">false</property>
+                            <property name="hexpand">true</property>
+                            <property name="action-name">file-settings.tab-width</property>
+                            <property name="action-target">uint32 2</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkToggleButton">
+                            <property name="visible">true</property>
+                            <property name="label" translatable="yes">3</property>
+                            <property name="focus-on-click">false</property>
+                            <property name="hexpand">true</property>
+                            <property name="action-name">file-settings.tab-width</property>
+                            <property name="action-target">uint32 3</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkToggleButton">
+                            <property name="visible">true</property>
+                            <property name="label" translatable="yes">4</property>
+                            <property name="focus-on-click">false</property>
+                            <property name="hexpand">true</property>
+                            <property name="action-name">file-settings.tab-width</property>
+                            <property name="action-target">uint32 4</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkToggleButton">
+                            <property name="visible">true</property>
+                            <property name="label" translatable="yes">8</property>
+                            <property name="focus-on-click">false</property>
+                            <property name="hexpand">true</property>
+                            <property name="action-name">file-settings.tab-width</property>
+                            <property name="action-target">uint32 8</property>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="top-attach">1</property>
+                        <property name="left-attach">1</property>
+                        <property name="width">1</property>
+                        <property name="height">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkBox">
+                        <property name="visible">true</property>
+                        <property name="hexpand">true</property>
+                        <property name="orientation">horizontal</property>
+                        <style>
+                          <class name="linked"/>
+                        </style>
+                        <child>
+                          <object class="GtkToggleButton">
+                            <property name="draw-indicator">false</property>
+                            <property name="visible">true</property>
+                            <property name="label" translatable="yes">Spaces</property>
+                            <property name="focus-on-click">false</property>
+                            <property name="hexpand">true</property>
+                            <property name="action-name">file-settings.indent-style</property>
+                            <property name="action-target">'spaces'</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkToggleButton" id="tabs_button">
+                            <property name="draw-indicator">false</property>
+                            <property name="visible">true</property>
+                            <property name="label" translatable="yes">Tabs</property>
+                            <property name="focus-on-click">false</property>
+                            <property name="hexpand">true</property>
+                            <property name="action-name">file-settings.indent-style</property>
+                            <property name="action-target">'tabs'</property>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="top-attach">0</property>
+                        <property name="left-attach">1</property>
+                        <property name="width">1</property>
+                        <property name="height">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="label" translatable="yes">Spaces per tab</property>
+                        <property name="visible">true</property>
+                        <property name="xalign">0.0</property>
+                        <property name="valign">baseline</property>
+                        <attributes>
+                          <attribute name="weight" value="bold"/>
+                        </attributes>
+                      </object>
+                      <packing>
+                        <property name="top-attach">1</property>
+                        <property name="left-attach">0</property>
+                        <property name="width">1</property>
+                        <property name="height">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/libide/editor/ide-editor-sidebar.c b/src/libide/editor/ide-editor-sidebar.c
index c3723c0d1..13c746f2a 100644
--- a/src/libide/editor/ide-editor-sidebar.c
+++ b/src/libide/editor/ide-editor-sidebar.c
@@ -23,12 +23,12 @@
 #include "config.h"
 
 #include <dazzle.h>
+#include <libide-gui.h>
 
-#include "editor/ide-editor-private.h"
-#include "editor/ide-editor-sidebar.h"
-#include "layout/ide-layout-private.h"
-#include "layout/ide-layout-stack.h"
-#include "layout/ide-layout-view.h"
+#include "ide-gui-private.h"
+
+#include "ide-editor-private.h"
+#include "ide-editor-sidebar.h"
 
 /**
  * SECTION:ide-editor-sidebar
@@ -36,7 +36,7 @@
  * @short_description: The left sidebar for the editor
  *
  * The #IdeEditorSidebar is the widget displayed on the left of the
- * #IdeEditorPerspective.  It contains an open document list, and then the
+ * #IdeEditorSurface.  It contains an open document list, and then the
  * various sections that have been added to the sidebar.
  *
  * Use ide_editor_sidebar_add_section() to add a section to the sidebar.
@@ -46,7 +46,7 @@
 
 struct _IdeEditorSidebar
 {
-  IdeLayoutPane      parent_instance;
+  IdePanel           parent_instance;
 
   GSettings         *settings;
   GListModel        *open_pages;
@@ -61,7 +61,7 @@ struct _IdeEditorSidebar
   GtkStack          *stack;
 };
 
-G_DEFINE_TYPE (IdeEditorSidebar, ide_editor_sidebar, IDE_TYPE_LAYOUT_PANE)
+G_DEFINE_TYPE (IdeEditorSidebar, ide_editor_sidebar, IDE_TYPE_PANEL)
 
 static void
 ide_editor_sidebar_update_title (IdeEditorSidebar *self)
@@ -118,20 +118,20 @@ ide_editor_sidebar_open_pages_row_activated (IdeEditorSidebar *self,
                                              GtkListBoxRow    *row,
                                              GtkListBox       *list_box)
 {
-  IdeLayoutView *view;
+  IdePage *view;
   GtkWidget *stack;
 
   g_assert (IDE_IS_EDITOR_SIDEBAR (self));
   g_assert (GTK_IS_LIST_BOX_ROW (row));
   g_assert (GTK_IS_LIST_BOX (list_box));
 
-  view = g_object_get_data (G_OBJECT (row), "IDE_LAYOUT_VIEW");
-  g_assert (IDE_IS_LAYOUT_VIEW (view));
+  view = g_object_get_data (G_OBJECT (row), "IDE_PAGE");
+  g_assert (IDE_IS_PAGE (view));
 
-  stack = gtk_widget_get_ancestor (GTK_WIDGET (view), IDE_TYPE_LAYOUT_STACK);
-  g_assert (IDE_IS_LAYOUT_STACK (stack));
+  stack = gtk_widget_get_ancestor (GTK_WIDGET (view), IDE_TYPE_FRAME);
+  g_assert (IDE_IS_FRAME (stack));
 
-  ide_layout_stack_set_visible_child (IDE_LAYOUT_STACK (stack), view);
+  ide_frame_set_visible_child (IDE_FRAME (stack), view);
 
   gtk_widget_grab_focus (GTK_WIDGET (view));
 }
@@ -194,7 +194,7 @@ ide_editor_sidebar_class_init (IdeEditorSidebarClass *klass)
 
   widget_class->destroy = ide_editor_sidebar_destroy;
 
-  gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/builder/ui/ide-editor-sidebar.ui");
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/libide-editor/ui/ide-editor-sidebar.ui");
   gtk_widget_class_bind_template_child (widget_class, IdeEditorSidebar, box);
   gtk_widget_class_bind_template_child (widget_class, IdeEditorSidebar, open_pages_list_box);
   gtk_widget_class_bind_template_child (widget_class, IdeEditorSidebar, open_pages_section);
@@ -397,37 +397,37 @@ ide_editor_sidebar_set_section_id (IdeEditorSidebar *self,
 
 static void
 ide_editor_sidebar_close_view (GtkButton     *button,
-                               IdeLayoutView *view)
+                               IdePage *view)
 {
   GtkWidget *stack;
 
   g_assert (GTK_IS_BUTTON (button));
-  g_assert (IDE_IS_LAYOUT_VIEW (view));
+  g_assert (IDE_IS_PAGE (view));
 
-  stack = gtk_widget_get_ancestor (GTK_WIDGET (view), IDE_TYPE_LAYOUT_STACK);
+  stack = gtk_widget_get_ancestor (GTK_WIDGET (view), IDE_TYPE_FRAME);
 
   if (stack != NULL)
-    _ide_layout_stack_request_close (IDE_LAYOUT_STACK (stack), view);
+    _ide_frame_request_close (IDE_FRAME (stack), view);
 }
 
 static GtkWidget *
 create_open_page_row (gpointer item,
                       gpointer user_data)
 {
-  IdeLayoutView *view = item;
+  IdePage *view = item;
   GtkListBoxRow *row;
   GtkButton *button;
   GtkImage *image;
   GtkLabel *label;
   GtkBox *box;
 
-  g_assert (IDE_IS_LAYOUT_VIEW (view));
+  g_assert (IDE_IS_PAGE (view));
   g_assert (IDE_IS_EDITOR_SIDEBAR (user_data));
 
   row = g_object_new (GTK_TYPE_LIST_BOX_ROW,
                       "visible", TRUE,
                       NULL);
-  g_object_set_data (G_OBJECT (row), "IDE_LAYOUT_VIEW", view);
+  g_object_set_data (G_OBJECT (row), "IDE_PAGE", view);
 
   box = g_object_new (GTK_TYPE_BOX,
                       "orientation", GTK_ORIENTATION_HORIZONTAL,
@@ -480,7 +480,7 @@ create_open_page_row (gpointer item,
  * @open_pages: a #GListModel describing the open pages
  *
  * This private function is used to set the GListModel to use for the list
- * of open pages in the sidebar. It should contain a list of IdeLayoutView
+ * of open pages in the sidebar. It should contain a list of IdePage
  * which we will use to keep the rows up to date.
  *
  * Since: 3.32
@@ -492,7 +492,7 @@ _ide_editor_sidebar_set_open_pages (IdeEditorSidebar *self,
   g_return_if_fail (IDE_IS_EDITOR_SIDEBAR (self));
   g_return_if_fail (!open_pages || G_IS_LIST_MODEL (open_pages));
   g_return_if_fail (!open_pages ||
-                    g_list_model_get_item_type (open_pages) == IDE_TYPE_LAYOUT_VIEW);
+                    g_list_model_get_item_type (open_pages) == IDE_TYPE_PAGE);
 
   g_set_object (&self->open_pages, open_pages);
 
diff --git a/src/libide/editor/ide-editor-sidebar.h b/src/libide/editor/ide-editor-sidebar.h
index 8b0fc5064..746c5491c 100644
--- a/src/libide/editor/ide-editor-sidebar.h
+++ b/src/libide/editor/ide-editor-sidebar.h
@@ -20,16 +20,19 @@
 
 #pragma once
 
-#include "ide-version-macros.h"
+#if !defined (IDE_EDITOR_INSIDE) && !defined (IDE_EDITOR_COMPILATION)
+# error "Only <libide-editor.h> can be included directly."
+#endif
 
-#include "layout/ide-layout-pane.h"
+#include <libide-core.h>
+#include <libide-gui.h>
 
 G_BEGIN_DECLS
 
 #define IDE_TYPE_EDITOR_SIDEBAR (ide_editor_sidebar_get_type())
 
 IDE_AVAILABLE_IN_3_32
-G_DECLARE_FINAL_TYPE (IdeEditorSidebar, ide_editor_sidebar, IDE, EDITOR_SIDEBAR, IdeLayoutPane)
+G_DECLARE_FINAL_TYPE (IdeEditorSidebar, ide_editor_sidebar, IDE, EDITOR_SIDEBAR, IdePanel)
 
 IDE_AVAILABLE_IN_3_32
 GtkWidget   *ide_editor_sidebar_new            (void);
diff --git a/src/libide/editor/ide-editor-sidebar.ui b/src/libide/editor/ide-editor-sidebar.ui
index 149354164..341bd98f4 100644
--- a/src/libide/editor/ide-editor-sidebar.ui
+++ b/src/libide/editor/ide-editor-sidebar.ui
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <template class="IdeEditorSidebar" parent="IdeLayoutPane">
+  <template class="IdeEditorSidebar" parent="IdePanel">
     <child>
       <object class="GtkBox" id="box">
         <property name="vexpand">true</property>
diff --git a/src/libide/editor/ide-editor-perspective-actions.c 
b/src/libide/editor/ide-editor-surface-actions.c
similarity index 56%
rename from src/libide/editor/ide-editor-perspective-actions.c
rename to src/libide/editor/ide-editor-surface-actions.c
index 6661c6235..7e88e64f7 100644
--- a/src/libide/editor/ide-editor-perspective-actions.c
+++ b/src/libide/editor/ide-editor-surface-actions.c
@@ -1,4 +1,4 @@
-/* ide-editor-perspective-actions.c
+/* ide-editor-surface-actions.c
  *
  * Copyright 2017-2019 Christian Hergert <chergert redhat com>
  *
@@ -18,61 +18,59 @@
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
 
-#define G_LOG_DOMAIN "ide-editor-perspective-actions"
+#define G_LOG_DOMAIN "ide-editor-surface-actions"
 
 #include "config.h"
 
 #include <glib/gi18n.h>
 
-#include "buffers/ide-buffer-manager.h"
-#include "editor/ide-editor-private.h"
-#include "util/ide-gtk.h"
+#include "ide-editor-private.h"
 
 static void
-ide_editor_perspective_actions_new_file (GSimpleAction *action,
-                                         GVariant      *variant,
-                                         gpointer       user_data)
+ide_editor_surface_actions_new_file (GSimpleAction *action,
+                                     GVariant      *variant,
+                                     gpointer       user_data)
 {
-  IdeEditorPerspective *self = user_data;
-  IdeWorkbench *workbench;
-  IdeContext *context;
+  IdeEditorSurface *self = user_data;
   IdeBufferManager *bufmgr;
-  IdeBuffer *buffer;
+  IdeContext *context;
 
   g_assert (G_IS_SIMPLE_ACTION (action));
-  g_assert (IDE_IS_EDITOR_PERSPECTIVE (self));
-
-  workbench = ide_widget_get_workbench (GTK_WIDGET (self));
-  context = ide_workbench_get_context (workbench);
-  bufmgr = ide_context_get_buffer_manager (context);
-  buffer = ide_buffer_manager_create_temporary_buffer (bufmgr);
-
-  g_clear_object (&buffer);
+  g_assert (IDE_IS_EDITOR_SURFACE (self));
+
+  context = ide_widget_get_context (GTK_WIDGET (self));
+  bufmgr = ide_buffer_manager_from_context (context);
+
+  ide_buffer_manager_load_file_async (bufmgr,
+                                      NULL,
+                                      IDE_BUFFER_OPEN_FLAGS_NONE,
+                                      NULL,
+                                      NULL,
+                                      NULL,
+                                      NULL);
 }
 
 static void
-ide_editor_perspective_actions_open_file (GSimpleAction *action,
-                                          GVariant      *variant,
-                                          gpointer       user_data)
+ide_editor_surface_actions_open_file (GSimpleAction *action,
+                                      GVariant      *variant,
+                                      gpointer       user_data)
 {
-  IdeEditorPerspective *self = user_data;
+  IdeEditorSurface *self = user_data;
   GtkFileChooserNative *chooser;
   IdeWorkbench *workbench;
+  GtkWidget *workspace;
   gint ret;
 
   g_assert (G_IS_SIMPLE_ACTION (action));
-  g_assert (IDE_IS_EDITOR_PERSPECTIVE (self));
+  g_assert (IDE_IS_EDITOR_SURFACE (self));
 
-  workbench = ide_widget_get_workbench (GTK_WIDGET (self));
+  if (!(workbench = ide_widget_get_workbench (GTK_WIDGET (self))))
+    return;
 
-  if (workbench == NULL)
-    {
-      g_warning ("Failed to locate workbench");
-      return;
-    }
+  workspace = gtk_widget_get_toplevel (GTK_WIDGET (self));
 
   chooser = gtk_file_chooser_native_new (_("Open File"),
-                                         GTK_WINDOW (workbench),
+                                         GTK_WINDOW (workspace),
                                          GTK_FILE_CHOOSER_ACTION_OPEN,
                                          _("Open"),
                                          _("Cancel"));
@@ -93,51 +91,50 @@ ide_editor_perspective_actions_open_file (GSimpleAction *action,
       g_slist_free (files);
 
       if (ar->len > 0)
-        ide_workbench_open_files_async (workbench,
-                                        (GFile **)ar->pdata,
-                                        ar->len,
-                                        "editor",
-                                        IDE_WORKBENCH_OPEN_FLAGS_NONE,
-                                        NULL, NULL, NULL);
+        ide_workbench_open_all_async (workbench,
+                                      (GFile **)ar->pdata,
+                                      ar->len,
+                                      "editor",
+                                      NULL, NULL, NULL);
     }
 
   gtk_native_dialog_destroy (GTK_NATIVE_DIALOG (chooser));
 }
 
 static void
-collect_views (GtkWidget *widget,
+collect_pages (GtkWidget *widget,
                gpointer   user_data)
 {
-  IdeLayoutView *view = (IdeLayoutView *)widget;
+  IdePage *view = (IdePage *)widget;
   GPtrArray *views = user_data;
 
   g_assert (views != NULL);
-  g_assert (IDE_IS_LAYOUT_VIEW (view));
+  g_assert (IDE_IS_PAGE (view));
 
   g_ptr_array_add (views, g_object_ref (view));
 }
 
 static void
-ide_editor_perspective_actions_close_all (GSimpleAction *action,
-                                          GVariant      *param,
-                                          gpointer       user_data)
+ide_editor_surface_actions_close_all (GSimpleAction *action,
+                                      GVariant      *param,
+                                      gpointer       user_data)
 {
-  IdeEditorPerspective *self = user_data;
+  IdeEditorSurface *self = user_data;
   g_autoptr(GPtrArray) views = NULL;
 
   g_assert (G_IS_SIMPLE_ACTION (action));
-  g_assert (IDE_IS_EDITOR_PERSPECTIVE (self));
+  g_assert (IDE_IS_EDITOR_SURFACE (self));
 
   /* First collect all the views and hold a reference to them
    * so that we do not need to worry about contains being destroyed
    * as we work through the list.
    */
   views = g_ptr_array_new_full (0, g_object_unref);
-  ide_layout_grid_foreach_view (self->grid, collect_views, views);
+  ide_grid_foreach_page (self->grid, collect_pages, views);
 
   for (guint i = 0; i < views->len; i++)
     {
-      IdeLayoutView *view = g_ptr_array_index (views, i);
+      IdePage *view = g_ptr_array_index (views, i);
 
       /* TODO: Should we allow suspending the close with
        *       agree_to_close_async()?
@@ -148,13 +145,13 @@ ide_editor_perspective_actions_close_all (GSimpleAction *action,
 }
 
 static const GActionEntry editor_actions[] = {
-  { "new-file", ide_editor_perspective_actions_new_file },
-  { "open-file", ide_editor_perspective_actions_open_file },
-  { "close-all", ide_editor_perspective_actions_close_all },
+  { "new-file", ide_editor_surface_actions_new_file },
+  { "open-file", ide_editor_surface_actions_open_file },
+  { "close-all", ide_editor_surface_actions_close_all },
 };
 
 void
-_ide_editor_perspective_init_actions (IdeEditorPerspective *self)
+_ide_editor_surface_init_actions (IdeEditorSurface *self)
 {
   g_autoptr(GSimpleActionGroup) group = NULL;
 
diff --git a/src/libide/editor/ide-editor-perspective-shortcuts.c 
b/src/libide/editor/ide-editor-surface-shortcuts.c
similarity index 90%
rename from src/libide/editor/ide-editor-perspective-shortcuts.c
rename to src/libide/editor/ide-editor-surface-shortcuts.c
index a45c3afe4..1e037c3e6 100644
--- a/src/libide/editor/ide-editor-perspective-shortcuts.c
+++ b/src/libide/editor/ide-editor-surface-shortcuts.c
@@ -1,4 +1,4 @@
-/* ide-editor-perspective-shortcuts.c
+/* ide-editor-surface-shortcuts.c
  *
  * Copyright 2017-2019 Christian Hergert <chergert redhat com>
  *
@@ -18,18 +18,18 @@
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
 
-#define G_LOG_DOMAIN "ide-editor-perspective-shortcuts"
+#define G_LOG_DOMAIN "ide-editor-surface-shortcuts"
 
 #include "config.h"
 
 #include <glib/gi18n.h>
 #include <dazzle.h>
 
-#include "editor/ide-editor-private.h"
+#include "ide-editor-private.h"
 
 #define I_(s) g_intern_static_string(s)
 
-static const DzlShortcutEntry editor_perspective_entries[] = {
+static const DzlShortcutEntry editor_surface_entries[] = {
   { "org.gnome.builder.editor.new-file",
     0, NULL,
     NC_("shortcut window", "Editor shortcuts"),
@@ -62,11 +62,11 @@ static const DzlShortcutEntry editor_perspective_entries[] = {
 };
 
 void
-_ide_editor_perspective_init_shortcuts (IdeEditorPerspective *self)
+_ide_editor_surface_init_shortcuts (IdeEditorSurface *self)
 {
   DzlShortcutController *controller;
 
-  g_assert (IDE_IS_EDITOR_PERSPECTIVE (self));
+  g_assert (IDE_IS_EDITOR_SURFACE (self));
 
   controller = dzl_shortcut_controller_find (GTK_WIDGET (self));
 
@@ -101,7 +101,7 @@ _ide_editor_perspective_init_shortcuts (IdeEditorPerspective *self)
                                               I_("editor.close-all"));
 
   dzl_shortcut_manager_add_shortcut_entries (NULL,
-                                             editor_perspective_entries,
-                                             G_N_ELEMENTS (editor_perspective_entries),
+                                             editor_surface_entries,
+                                             G_N_ELEMENTS (editor_surface_entries),
                                              GETTEXT_PACKAGE);
 }
diff --git a/src/libide/editor/ide-editor-surface.c b/src/libide/editor/ide-editor-surface.c
new file mode 100644
index 000000000..78f3ffde4
--- /dev/null
+++ b/src/libide/editor/ide-editor-surface.c
@@ -0,0 +1,919 @@
+/* ide-editor-surface.c
+ *
+ * Copyright 2017-2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-editor-surface"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <libide-code.h>
+
+#include "ide-editor-addin.h"
+#include "ide-editor-surface.h"
+#include "ide-editor-private.h"
+#include "ide-editor-sidebar.h"
+#include "ide-editor-utilities.h"
+#include "ide-editor-page.h"
+
+typedef struct
+{
+  IdeEditorSurface *self;
+  IdeLocation      *location;
+} FocusLocation;
+
+static void ide_editor_surface_focus_location_full (IdeEditorSurface *self,
+                                                    IdeLocation      *location,
+                                                    gboolean          open_if_not_found);
+
+G_DEFINE_TYPE (IdeEditorSurface, ide_editor_surface, IDE_TYPE_SURFACE)
+
+static void
+ide_editor_surface_foreach_page (IdeSurface  *surface,
+                                 GtkCallback  callback,
+                                 gpointer     user_data)
+{
+  IdeEditorSurface *self = (IdeEditorSurface *)surface;
+
+  g_assert (IDE_IS_EDITOR_SURFACE (self));
+  g_assert (callback != NULL);
+
+  ide_grid_foreach_page (self->grid, callback, user_data);
+}
+
+static void
+set_reveal_child_without_transition (DzlDockRevealer *revealer,
+                                     gboolean         reveal)
+{
+  DzlDockRevealerTransitionType type;
+
+  g_assert (DZL_IS_DOCK_REVEALER (revealer));
+
+  type = dzl_dock_revealer_get_transition_type (revealer);
+  dzl_dock_revealer_set_transition_type (revealer, DZL_DOCK_REVEALER_TRANSITION_TYPE_NONE);
+  dzl_dock_revealer_set_reveal_child (revealer, reveal);
+  dzl_dock_revealer_set_transition_type (revealer, type);
+}
+
+static void
+ide_editor_surface_restore_panel_state (IdeEditorSurface *self)
+{
+  g_autoptr(GSettings) settings = NULL;
+  GtkWidget *pane;
+  gboolean reveal;
+  guint position;
+
+  g_assert (IDE_IS_EDITOR_SURFACE (self));
+
+  /* TODO: This belongs in editor settings probably */
+
+  settings = g_settings_new ("org.gnome.builder.workbench");
+
+  pane = dzl_dock_bin_get_left_edge (DZL_DOCK_BIN (self));
+  reveal = g_settings_get_boolean (settings, "left-visible");
+  position = g_settings_get_int (settings, "left-position");
+  dzl_dock_revealer_set_position (DZL_DOCK_REVEALER (pane), position);
+  set_reveal_child_without_transition (DZL_DOCK_REVEALER (pane), reveal);
+
+  pane = dzl_dock_bin_get_right_edge (DZL_DOCK_BIN (self));
+  position = g_settings_get_int (settings, "right-position");
+  dzl_dock_revealer_set_position (DZL_DOCK_REVEALER (pane), position);
+  set_reveal_child_without_transition (DZL_DOCK_REVEALER (pane), FALSE);
+
+  pane = dzl_dock_bin_get_bottom_edge (DZL_DOCK_BIN (self));
+  reveal = g_settings_get_boolean (settings, "bottom-visible");
+  position = g_settings_get_int (settings, "bottom-position");
+  dzl_dock_revealer_set_position (DZL_DOCK_REVEALER (pane), position);
+  set_reveal_child_without_transition (DZL_DOCK_REVEALER (pane), reveal);
+}
+
+static void
+ide_editor_surface_save_panel_state (IdeEditorSurface *self)
+{
+  g_autoptr(GSettings) settings = NULL;
+  GtkWidget *pane;
+  gboolean reveal;
+  guint position;
+
+  g_assert (IDE_IS_EDITOR_SURFACE (self));
+
+  /* TODO: possibly belongs in editor settings */
+  settings = g_settings_new ("org.gnome.builder.workbench");
+
+  pane = dzl_dock_bin_get_left_edge (DZL_DOCK_BIN (self));
+  position = dzl_dock_revealer_get_position (DZL_DOCK_REVEALER (pane));
+  reveal = dzl_dock_revealer_get_reveal_child (DZL_DOCK_REVEALER (pane));
+  g_settings_set_boolean (settings, "left-visible", reveal);
+  g_settings_set_int (settings, "left-position", position);
+
+  pane = dzl_dock_bin_get_right_edge (DZL_DOCK_BIN (self));
+  position = dzl_dock_revealer_get_position (DZL_DOCK_REVEALER (pane));
+  reveal = dzl_dock_revealer_get_reveal_child (DZL_DOCK_REVEALER (pane));
+  g_settings_set_boolean (settings, "right-visible", reveal);
+  g_settings_set_int (settings, "right-position", position);
+
+  pane = dzl_dock_bin_get_bottom_edge (DZL_DOCK_BIN (self));
+  position = dzl_dock_revealer_get_position (DZL_DOCK_REVEALER (pane));
+  reveal = dzl_dock_revealer_get_reveal_child (DZL_DOCK_REVEALER (pane));
+  g_settings_set_boolean (settings, "bottom-visible", reveal);
+  g_settings_set_int (settings, "bottom-position", position);
+}
+
+static gboolean
+ide_editor_surface_agree_to_shutdown (IdeSurface *surface)
+{
+  IdeEditorSurface *self = (IdeEditorSurface *)surface;
+
+  g_assert (IDE_IS_EDITOR_SURFACE (self));
+
+  ide_editor_surface_save_panel_state (self);
+
+  return TRUE;
+}
+
+static void
+ide_editor_surface_set_fullscreen (IdeSurface *surface,
+                                   gboolean    fullscreen)
+{
+  IdeEditorSurface *self = (IdeEditorSurface *)surface;
+
+  g_assert (IDE_IS_EDITOR_SURFACE (self));
+
+  if (fullscreen)
+    {
+      gboolean left_visible;
+      gboolean bottom_visible;
+
+      g_object_get (self,
+                    "left-visible", &left_visible,
+                    "bottom-visible", &bottom_visible,
+                    NULL);
+
+      self->prefocus_had_left = left_visible;
+      self->prefocus_had_bottom = bottom_visible;
+
+      g_object_set (self,
+                    "left-visible", FALSE,
+                    "bottom-visible", FALSE,
+                    NULL);
+    }
+  else
+    {
+      g_object_set (self,
+                    "left-visible", self->prefocus_had_left,
+                    "bottom-visible", self->prefocus_had_bottom,
+                    NULL);
+    }
+}
+
+
+static void
+ide_editor_surface_addin_added (PeasExtensionSet *set,
+                                PeasPluginInfo   *plugin_info,
+                                PeasExtension    *exten,
+                                gpointer          user_data)
+{
+  IdeEditorSurface *self = user_data;
+  IdeEditorAddin *addin = (IdeEditorAddin *)exten;
+  IdePage *page;
+
+  g_assert (IDE_IS_EDITOR_SURFACE (self));
+  g_assert (IDE_IS_EDITOR_ADDIN (addin));
+  g_assert (PEAS_IS_EXTENSION_SET (set));
+  g_assert (plugin_info != NULL);
+
+  ide_editor_addin_load (addin, self);
+
+  page = ide_grid_get_current_page (self->grid);
+  if (page != NULL)
+    ide_editor_addin_page_set (addin, page);
+}
+
+static void
+ide_editor_surface_addin_removed (PeasExtensionSet *set,
+                                      PeasPluginInfo   *plugin_info,
+                                      PeasExtension    *exten,
+                                      gpointer          user_data)
+{
+  IdeEditorSurface *self = user_data;
+  IdeEditorAddin *addin = (IdeEditorAddin *)exten;
+  IdePage *page;
+
+  g_assert (IDE_IS_EDITOR_SURFACE (self));
+  g_assert (IDE_IS_EDITOR_ADDIN (addin));
+  g_assert (PEAS_IS_EXTENSION_SET (set));
+  g_assert (plugin_info != NULL);
+
+  page = ide_grid_get_current_page (self->grid);
+  if (page != NULL)
+    ide_editor_addin_page_set (addin, NULL);
+
+  ide_editor_addin_unload (addin, self);
+}
+
+static void
+ide_editor_surface_hierarchy_changed (GtkWidget *widget,
+                                      GtkWidget *old_toplevel)
+{
+  IdeEditorSurface *self = (IdeEditorSurface *)widget;
+
+  g_assert (IDE_IS_EDITOR_SURFACE (self));
+  g_assert (!old_toplevel || GTK_IS_WIDGET (old_toplevel));
+
+  if (self->addins == NULL)
+    {
+      GtkWidget *toplevel;
+
+      /*
+       * If we just got a new toplevel and it is a workbench,
+       * and we have not yet created our addins, do so now.
+       */
+
+      toplevel = gtk_widget_get_ancestor (widget, IDE_TYPE_WORKSPACE);
+
+      if (toplevel != NULL)
+        {
+          self->addins = peas_extension_set_new (peas_engine_get_default (),
+                                                 IDE_TYPE_EDITOR_ADDIN,
+                                                 NULL);
+          g_signal_connect (self->addins,
+                            "extension-added",
+                            G_CALLBACK (ide_editor_surface_addin_added),
+                            self);
+          g_signal_connect (self->addins,
+                            "extension-removed",
+                            G_CALLBACK (ide_editor_surface_addin_removed),
+                            self);
+          peas_extension_set_foreach (self->addins,
+                                      ide_editor_surface_addin_added,
+                                      self);
+        }
+    }
+}
+
+static void
+ide_editor_surface_addins_page_set (PeasExtensionSet *set,
+                                    PeasPluginInfo   *plugin_info,
+                                    PeasExtension    *exten,
+                                    gpointer          user_data)
+{
+  IdeEditorAddin *addin = (IdeEditorAddin *)exten;
+  IdePage *page = user_data;
+
+  g_assert (PEAS_IS_EXTENSION_SET (set));
+  g_assert (plugin_info != NULL);
+  g_assert (IDE_IS_EDITOR_ADDIN (addin));
+  g_assert (!page || IDE_IS_PAGE (page));
+
+  ide_editor_addin_page_set (addin, page);
+}
+
+static void
+ide_editor_surface_notify_current_page (IdeEditorSurface *self,
+                                        GParamSpec       *pspec,
+                                        IdeGrid          *grid)
+{
+  IdePage *page;
+
+  g_assert (IDE_IS_EDITOR_SURFACE (self));
+  g_assert (pspec != NULL);
+  g_assert (IDE_IS_GRID (grid));
+
+  page = ide_grid_get_current_page (grid);
+
+  if (self->addins != NULL)
+    peas_extension_set_foreach (self->addins,
+                                ide_editor_surface_addins_page_set,
+                                page);
+}
+
+static void
+ide_editor_surface_add (GtkContainer *container,
+                            GtkWidget    *widget)
+{
+  IdeEditorSurface *self = (IdeEditorSurface *)container;
+
+  g_assert (IDE_IS_EDITOR_SURFACE (self));
+  g_assert (GTK_IS_WIDGET (widget));
+
+  if (IDE_IS_PAGE (widget))
+    gtk_container_add (GTK_CONTAINER (self->grid), widget);
+  else
+    GTK_CONTAINER_CLASS (ide_editor_surface_parent_class)->add (container, widget);
+}
+
+static GtkWidget *
+ide_editor_surface_create_edge (DzlDockBin      *dock_bin,
+                                GtkPositionType  edge)
+{
+  g_assert (DZL_IS_DOCK_BIN (dock_bin));
+  g_assert (edge >= GTK_POS_LEFT);
+  g_assert (edge <= GTK_POS_BOTTOM);
+
+  if (edge == GTK_POS_LEFT)
+    return g_object_new (IDE_TYPE_EDITOR_SIDEBAR,
+                         "edge", edge,
+                         "transition-duration", 333,
+                         "reveal-child", FALSE,
+                         "visible", TRUE,
+                         NULL);
+
+  if (edge == GTK_POS_RIGHT)
+    return g_object_new (IDE_TYPE_TRANSIENT_SIDEBAR,
+                         "edge", edge,
+                         "reveal-child", FALSE,
+                         "transition-duration", 333,
+                         "visible", FALSE,
+                         NULL);
+
+  if (edge == GTK_POS_BOTTOM)
+    return g_object_new (IDE_TYPE_EDITOR_UTILITIES,
+                         "edge", edge,
+                         "reveal-child", FALSE,
+                         "transition-duration", 333,
+                         "visible", TRUE,
+                         NULL);
+
+  return DZL_DOCK_BIN_CLASS (ide_editor_surface_parent_class)->create_edge (dock_bin, edge);
+}
+
+static void
+ide_editor_surface_load_file_cb (GObject      *object,
+                                     GAsyncResult *result,
+                                     gpointer      user_data)
+{
+  IdeBufferManager *bufmgr = (IdeBufferManager *)object;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(IdeBuffer) buffer = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_BUFFER_MANAGER (bufmgr));
+  g_assert (G_IS_ASYNC_RESULT (result));
+
+  buffer = ide_buffer_manager_load_file_finish (bufmgr, result, &error);
+
+  if (error != NULL)
+    g_warning ("%s", error->message);
+
+  /* TODO: Ensure that the page is marked as failed */
+
+  IDE_EXIT;
+}
+
+static IdePage *
+ide_editor_surface_create_page (IdeEditorSurface *self,
+                                const gchar      *uri,
+                                IdeGrid          *grid)
+{
+  g_autoptr(GFile) file = NULL;
+  IdeBufferManager *bufmgr;
+  IdeContext *context;
+  IdeBuffer *buffer;
+
+  g_assert (IDE_IS_EDITOR_SURFACE (self));
+  g_assert (uri != NULL);
+  g_assert (IDE_IS_GRID (grid));
+
+  g_debug ("Creating page for %s", uri);
+
+  context = ide_widget_get_context (GTK_WIDGET (self));
+
+  file = g_file_new_for_uri (uri);
+  bufmgr = ide_buffer_manager_from_context (context);
+  buffer = ide_buffer_manager_find_buffer (bufmgr, file);
+
+  /*
+   * If we failed to locate an already loaded buffer, we need to start
+   * loading the buffer. But that could take some time. Either way, after
+   * we start the loading process, we can access the buffer and we'll
+   * display it while it loads.
+   */
+
+  if (buffer == NULL)
+    {
+      ide_buffer_manager_load_file_async (bufmgr,
+                                          file,
+                                          IDE_BUFFER_OPEN_FLAGS_NO_VIEW,
+                                          NULL,
+                                          NULL,
+                                          ide_editor_surface_load_file_cb,
+                                          g_object_ref (self));
+      buffer = ide_buffer_manager_find_buffer (bufmgr, file);
+    }
+
+  return g_object_new (IDE_TYPE_EDITOR_PAGE,
+                       "buffer", buffer,
+                       "visible", TRUE,
+                       NULL);
+}
+
+static void
+ide_editor_surface_grab_focus (GtkWidget *widget)
+{
+  IdeEditorSurface *self = (IdeEditorSurface *)widget;
+
+  g_assert (IDE_IS_EDITOR_SURFACE (self));
+
+  gtk_widget_grab_focus (GTK_WIDGET (self->grid));
+}
+
+static void
+ide_editor_surface_destroy (GtkWidget *widget)
+{
+  IdeEditorSurface *self = (IdeEditorSurface *)widget;
+
+  g_assert (IDE_IS_EDITOR_SURFACE (self));
+
+  g_clear_object (&self->addins);
+
+  GTK_WIDGET_CLASS (ide_editor_surface_parent_class)->destroy (widget);
+}
+
+static void
+ide_editor_surface_realize (GtkWidget *widget)
+{
+  IdeEditorSurface *self = (IdeEditorSurface *)widget;
+
+  g_assert (IDE_IS_EDITOR_SURFACE (self));
+
+  ide_editor_surface_restore_panel_state (self);
+
+  GTK_WIDGET_CLASS (ide_editor_surface_parent_class)->realize (widget);
+}
+
+static void
+ide_editor_surface_class_init (IdeEditorSurfaceClass *klass)
+{
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
+  DzlDockBinClass *dock_bin_class = DZL_DOCK_BIN_CLASS (klass);
+  IdeSurfaceClass *surface_class = IDE_SURFACE_CLASS (klass);
+
+  widget_class->destroy = ide_editor_surface_destroy;
+  widget_class->hierarchy_changed = ide_editor_surface_hierarchy_changed;
+  widget_class->grab_focus = ide_editor_surface_grab_focus;
+  widget_class->realize = ide_editor_surface_realize;
+
+  container_class->add = ide_editor_surface_add;
+
+  dock_bin_class->create_edge = ide_editor_surface_create_edge;
+
+  surface_class->agree_to_shutdown = ide_editor_surface_agree_to_shutdown;
+  surface_class->foreach_page = ide_editor_surface_foreach_page;
+  surface_class->set_fullscreen = ide_editor_surface_set_fullscreen;
+
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/libide-editor/ui/ide-editor-surface.ui");
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorSurface, grid);
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorSurface, overlay);
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorSurface, loading_stack);
+
+  g_type_ensure (IDE_TYPE_EDITOR_SIDEBAR);
+  g_type_ensure (IDE_TYPE_GRID);
+}
+
+static void
+ide_editor_surface_init (IdeEditorSurface *self)
+{
+  IdeEditorSidebar *sidebar;
+
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  ide_surface_set_icon_name (IDE_SURFACE (self), "builder-editor-symbolic");
+  ide_surface_set_title (IDE_SURFACE (self), _("Editor"));
+
+  _ide_editor_surface_init_actions (self);
+  _ide_editor_surface_init_shortcuts (self);
+
+  /* ensure we default to the grid visible */
+  _ide_editor_surface_set_loading (self, FALSE);
+
+  g_signal_connect_swapped (self->grid,
+                            "notify::current-page",
+                            G_CALLBACK (ide_editor_surface_notify_current_page),
+                            self);
+
+  g_signal_connect_swapped (self->grid,
+                            "create-page",
+                            G_CALLBACK (ide_editor_surface_create_page),
+                            self);
+
+  sidebar = ide_editor_surface_get_sidebar (self);
+  _ide_editor_sidebar_set_open_pages (sidebar, G_LIST_MODEL (self->grid));
+}
+
+/**
+ * ide_editor_surface_get_grid:
+ * @self: a #IdeEditorSurface
+ *
+ * Gets the grid for the surface. This is the area containing
+ * grid columns, stacks, and pages.
+ *
+ * Returns: (transfer none): An #IdeGrid.
+ *
+ * Since: 3.32
+ */
+IdeGrid *
+ide_editor_surface_get_grid (IdeEditorSurface *self)
+{
+  g_return_val_if_fail (IDE_IS_EDITOR_SURFACE (self), NULL);
+
+  return self->grid;
+}
+
+static void
+ide_editor_surface_find_source_location (GtkWidget *widget,
+                                         gpointer   user_data)
+{
+  struct {
+    GFile *file;
+    IdeEditorPage *page;
+  } *lookup = user_data;
+  IdeBuffer *buffer;
+  GFile *file;
+
+  g_return_if_fail (IDE_IS_PAGE (widget));
+
+  if (lookup->page != NULL)
+    return;
+
+  if (!IDE_IS_EDITOR_PAGE (widget))
+    return;
+
+  buffer = ide_editor_page_get_buffer (IDE_EDITOR_PAGE (widget));
+  file = ide_buffer_get_file (buffer);
+
+  if (g_file_equal (file, lookup->file))
+    lookup->page = IDE_EDITOR_PAGE (widget);
+}
+
+static void
+ide_editor_surface_focus_location_cb (GObject      *object,
+                                      GAsyncResult *result,
+                                      gpointer      user_data)
+{
+  IdeBufferManager *bufmgr = (IdeBufferManager *)object;
+  g_autoptr(IdeBuffer) buffer = NULL;
+  FocusLocation *state = user_data;
+  GError *error = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_BUFFER_MANAGER (bufmgr));
+  g_assert (state != NULL);
+  g_assert (IDE_IS_EDITOR_SURFACE (state->self));
+  g_assert (state->location != NULL);
+
+  if (!(buffer = ide_buffer_manager_load_file_finish (bufmgr, result, &error)))
+    {
+      /* TODO: display warning breifly to the user in the frame? */
+      g_warning ("%s", error->message);
+      g_clear_error (&error);
+      IDE_GOTO (cleanup);
+    }
+
+  /* try again now that we have loaded */
+  ide_editor_surface_focus_location_full (state->self, state->location, FALSE);
+
+cleanup:
+  g_clear_object (&state->self);
+  g_clear_object (&state->location);
+  g_slice_free (FocusLocation, state);
+
+  IDE_EXIT;
+}
+
+static void
+ide_editor_surface_focus_location_full (IdeEditorSurface  *self,
+                                        IdeLocation *location,
+                                        gboolean           open_if_not_found)
+{
+  struct {
+    GFile *file;
+    IdeEditorPage *page;
+  } lookup = { 0 };
+  GtkWidget *stack;
+  gint line;
+  gint line_offset;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_EDITOR_SURFACE (self));
+  g_assert (location != NULL);
+
+  lookup.file = ide_location_get_file (location);
+  lookup.page = NULL;
+
+  if (lookup.file == NULL)
+    {
+      g_warning ("IdeLocation does not contain a file");
+      IDE_EXIT;
+    }
+
+#ifdef IDE_ENABLE_TRACE
+  {
+    const gchar *path = g_file_peek_path (lookup.file);
+    IDE_TRACE_MSG ("Locating %s, open_if_not_found=%d",
+                   path, open_if_not_found);
+  }
+#endif
+
+  ide_surface_foreach_page (IDE_SURFACE (self),
+                            ide_editor_surface_find_source_location,
+                            &lookup);
+
+  if (!open_if_not_found && lookup.page == NULL)
+    IDE_EXIT;
+
+  if (lookup.page == NULL)
+    {
+      FocusLocation *state;
+      IdeBufferManager *bufmgr;
+      IdeWorkbench *workbench;
+      IdeContext *context;
+
+      workbench = ide_widget_get_workbench (GTK_WIDGET (self));
+      context = ide_workbench_get_context (workbench);
+      bufmgr = ide_buffer_manager_from_context (context);
+
+      state = g_slice_new0 (FocusLocation);
+      state->self = g_object_ref (self);
+      state->location = g_object_ref (location);
+
+      ide_buffer_manager_load_file_async (bufmgr,
+                                          lookup.file,
+                                          IDE_BUFFER_OPEN_FLAGS_NONE,
+                                          NULL,
+                                          NULL,
+                                          ide_editor_surface_focus_location_cb,
+                                          state);
+      IDE_EXIT;
+    }
+
+  line = ide_location_get_line (location);
+  line_offset = ide_location_get_line_offset (location);
+
+  stack = gtk_widget_get_ancestor (GTK_WIDGET (lookup.page), IDE_TYPE_FRAME);
+  ide_frame_set_visible_child (IDE_FRAME (stack), IDE_PAGE (lookup.page));
+
+  /*
+   * Ignore 0:0 so that we don't jump from the previous cursor position,
+   * if any. It's somewhat problematic if we know we need to go to 0:0,
+   * but that is less likely.
+   */
+  if (line > 0 || line_offset > 0)
+    ide_editor_page_scroll_to_line_offset (lookup.page,
+                                           MAX (line, 0),
+                                           MAX (line_offset, 0));
+  else
+    gtk_widget_grab_focus (GTK_WIDGET (lookup.page));
+
+  IDE_EXIT;
+}
+
+void
+ide_editor_surface_focus_location (IdeEditorSurface  *self,
+                                   IdeLocation *location)
+{
+  IDE_ENTRY;
+
+  g_return_if_fail (IDE_IS_EDITOR_SURFACE (self));
+  g_return_if_fail (location != NULL);
+
+  ide_editor_surface_focus_location_full (self, location, TRUE);
+
+  IDE_EXIT;
+}
+
+static void
+locate_page_for_buffer (GtkWidget *widget,
+                        gpointer   user_data)
+{
+  struct {
+    IdeBuffer *buffer;
+    IdePage   *page;
+  } *lookup = user_data;
+
+  if (lookup->page != NULL)
+    return;
+
+  if (IDE_IS_EDITOR_PAGE (widget))
+    {
+      if (ide_editor_page_get_buffer (IDE_EDITOR_PAGE (widget)) == lookup->buffer)
+        lookup->page = IDE_PAGE (widget);
+    }
+}
+
+static gboolean
+ide_editor_surface_focus_if_found (IdeEditorSurface *self,
+                                   IdeBuffer        *buffer,
+                                   gboolean          any_stack)
+{
+  IdeFrame *stack;
+  struct {
+    IdeBuffer *buffer;
+    IdePage   *page;
+  } lookup = { buffer };
+
+  g_return_val_if_fail (IDE_IS_EDITOR_SURFACE (self), FALSE);
+  g_return_val_if_fail (IDE_IS_BUFFER (buffer), FALSE);
+
+  stack = ide_grid_get_current_stack (self->grid);
+
+  if (any_stack)
+    ide_grid_foreach_page (self->grid, locate_page_for_buffer, &lookup);
+  else
+    ide_frame_foreach_page (stack, locate_page_for_buffer, &lookup);
+
+  if (lookup.page != NULL)
+    {
+      stack = IDE_FRAME (gtk_widget_get_ancestor (GTK_WIDGET (lookup.page), IDE_TYPE_FRAME));
+      ide_frame_set_visible_child (stack, lookup.page);
+      gtk_widget_grab_focus (GTK_WIDGET (lookup.page));
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+void
+ide_editor_surface_focus_buffer (IdeEditorSurface *self,
+                                 IdeBuffer        *buffer)
+{
+  IdeEditorPage *page;
+
+  IDE_ENTRY;
+
+  g_return_if_fail (IDE_IS_EDITOR_SURFACE (self));
+  g_return_if_fail (IDE_IS_BUFFER (buffer));
+
+  if (ide_editor_surface_focus_if_found (self, buffer, TRUE))
+    IDE_EXIT;
+
+  page = g_object_new (IDE_TYPE_EDITOR_PAGE,
+                       "buffer", buffer,
+                       "visible", TRUE,
+                       NULL);
+  gtk_container_add (GTK_CONTAINER (self->grid), GTK_WIDGET (page));
+
+  IDE_EXIT;
+}
+
+void
+ide_editor_surface_focus_buffer_in_current_stack (IdeEditorSurface *self,
+                                                  IdeBuffer        *buffer)
+{
+  IdeFrame *stack;
+  IdeEditorPage *page;
+
+  g_return_if_fail (IDE_IS_EDITOR_SURFACE (self));
+  g_return_if_fail (IDE_IS_BUFFER (buffer));
+
+  if (ide_editor_surface_focus_if_found (self, buffer, FALSE))
+    return;
+
+  stack = ide_grid_get_current_stack (self->grid);
+
+  page = g_object_new (IDE_TYPE_EDITOR_PAGE,
+                       "buffer", buffer,
+                       "visible", TRUE,
+                       NULL);
+
+  gtk_container_add (GTK_CONTAINER (stack), GTK_WIDGET (page));
+}
+
+/**
+ * ide_editor_surface_get_active_page:
+ * @self: a #IdeEditorSurface
+ *
+ * Gets the active page for the surface, or %NULL if there is not one.
+ *
+ * Returns: (nullable) (transfer none): An #IdePage or %NULL.
+ *
+ * Since: 3.32
+ */
+IdePage *
+ide_editor_surface_get_active_page (IdeEditorSurface *self)
+{
+  IdeFrame *stack;
+
+  g_return_val_if_fail (IDE_IS_EDITOR_SURFACE (self), NULL);
+
+  stack = ide_grid_get_current_stack (self->grid);
+
+  return ide_frame_get_visible_child (stack);
+}
+
+/**
+ * ide_editor_surface_get_sidebar:
+ * @self: a #IdeEditorSurface
+ *
+ * Gets the #IdeEditorSidebar for the editor surface.
+ *
+ * Returns: (transfer none): an #IdeEditorSidebar
+ *
+ * Since: 3.32
+ */
+IdeEditorSidebar *
+ide_editor_surface_get_sidebar (IdeEditorSurface *self)
+{
+  g_return_val_if_fail (IDE_IS_EDITOR_SURFACE (self), NULL);
+
+  return IDE_EDITOR_SIDEBAR (dzl_dock_bin_get_left_edge (DZL_DOCK_BIN (self)));
+}
+
+/**
+ * ide_editor_surface_get_transient_sidebar:
+ * @self: a #IdeEditorSurface
+ *
+ * Gets the transient sidebar for the editor surface.
+ *
+ * The transient sidebar is a sidebar on the right side of the surface. It
+ * is displayed only when necessary. It animates in and out of page based on
+ * focus tracking and other heuristics.
+ *
+ * Returns: (transfer none): An #IdeTransientSidebar
+ *
+ * Since: 3.32
+ */
+IdeTransientSidebar *
+ide_editor_surface_get_transient_sidebar (IdeEditorSurface *self)
+{
+  g_return_val_if_fail (IDE_IS_EDITOR_SURFACE (self), NULL);
+
+  return IDE_TRANSIENT_SIDEBAR (dzl_dock_bin_get_right_edge (DZL_DOCK_BIN (self)));
+}
+
+/**
+ * ide_editor_surface_get_utilities:
+ *
+ * Returns: (transfer none): An #IdeEditorUtilities
+ *
+ * Since: 3.32
+ */
+GtkWidget *
+ide_editor_surface_get_utilities (IdeEditorSurface *self)
+{
+  g_return_val_if_fail (IDE_IS_EDITOR_SURFACE (self), NULL);
+
+  return dzl_dock_bin_get_bottom_edge (DZL_DOCK_BIN (self));
+}
+
+/**
+ * ide_editor_surface_get_overlay:
+ * @self: a #IdeEditorSurface
+ *
+ * Gets the overlay widget which can be used to layer things above all
+ * items in the layout grid.
+ *
+ * Returns: (transfer none) (type Gtk.Overlay): a #GtkWidget
+ *
+ * Since: 3.32
+ */
+GtkWidget *
+ide_editor_surface_get_overlay (IdeEditorSurface *self)
+{
+  g_return_val_if_fail (IDE_IS_EDITOR_SURFACE (self), NULL);
+
+  return GTK_WIDGET (self->overlay);
+}
+
+void
+_ide_editor_surface_set_loading (IdeEditorSurface *self,
+                                 gboolean          loading)
+{
+  g_return_if_fail (IDE_IS_EDITOR_SURFACE (self));
+
+  gtk_widget_set_visible (GTK_WIDGET (self->grid), !loading);
+  gtk_stack_set_visible_child_name (self->loading_stack,
+                                    loading ? "empty_state" : "grid");
+}
+
+/**
+ * ide_editor_surface_new:
+ *
+ * Returns: (transfer full): Creates a new #IdeEditorSurface
+ *
+ * Since: 3.32
+ */
+IdeSurface *
+ide_editor_surface_new (void)
+{
+  return g_object_new (IDE_TYPE_EDITOR_SURFACE, NULL);
+}
diff --git a/src/libide/editor/ide-editor-surface.h b/src/libide/editor/ide-editor-surface.h
new file mode 100644
index 000000000..ea2a4eef6
--- /dev/null
+++ b/src/libide/editor/ide-editor-surface.h
@@ -0,0 +1,65 @@
+/* ide-editor-surface.h
+ *
+ * Copyright 2017-2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_EDITOR_INSIDE) && !defined (IDE_EDITOR_COMPILATION)
+# error "Only <idide-editor.h> can be included directly."
+#endif
+
+#include <dazzle.h>
+#include <libide-code.h>
+#include <libide-gui.h>
+
+#include "ide-editor-sidebar.h"
+#include "ide-editor-utilities.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_EDITOR_SURFACE (ide_editor_surface_get_type())
+
+IDE_AVAILABLE_IN_3_32
+G_DECLARE_FINAL_TYPE (IdeEditorSurface, ide_editor_surface, IDE, EDITOR_SURFACE, IdeSurface)
+
+IDE_AVAILABLE_IN_3_32
+IdeSurface          *ide_editor_surface_new                           (void);
+IDE_AVAILABLE_IN_3_32
+void                 ide_editor_surface_focus_buffer                  (IdeEditorSurface *self,
+                                                                       IdeBuffer        *buffer);
+IDE_AVAILABLE_IN_3_32
+void                 ide_editor_surface_focus_buffer_in_current_stack (IdeEditorSurface *self,
+                                                                       IdeBuffer        *buffer);
+IDE_AVAILABLE_IN_3_32
+void                 ide_editor_surface_focus_location                (IdeEditorSurface *self,
+                                                                       IdeLocation      *location);
+IDE_AVAILABLE_IN_3_32
+IdePage             *ide_editor_surface_get_active_page               (IdeEditorSurface *self);
+IDE_AVAILABLE_IN_3_32
+IdeGrid             *ide_editor_surface_get_grid                      (IdeEditorSurface *self);
+IDE_AVAILABLE_IN_3_32
+IdeEditorSidebar    *ide_editor_surface_get_sidebar                   (IdeEditorSurface *self);
+IDE_AVAILABLE_IN_3_32
+IdeTransientSidebar *ide_editor_surface_get_transient_sidebar         (IdeEditorSurface *self);
+IDE_AVAILABLE_IN_3_32
+GtkWidget           *ide_editor_surface_get_utilities                 (IdeEditorSurface *self);
+IDE_AVAILABLE_IN_3_32
+GtkWidget           *ide_editor_surface_get_overlay                   (IdeEditorSurface *self);
+
+G_END_DECLS
diff --git a/src/libide/editor/ide-editor-perspective.ui b/src/libide/editor/ide-editor-surface.ui
similarity index 74%
rename from src/libide/editor/ide-editor-perspective.ui
rename to src/libide/editor/ide-editor-surface.ui
index 0ba445b33..a65af292a 100644
--- a/src/libide/editor/ide-editor-perspective.ui
+++ b/src/libide/editor/ide-editor-surface.ui
@@ -1,16 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <template class="IdeEditorPerspective" parent="IdeLayout">
-    <child internal-child="right">
-      <object class="IdeLayoutTransientSidebar">
-        <child>
-          <object class="IdeEditorProperties" id="properties">
-            <property name="sensitive">false</property>
-            <property name="visible">true</property>
-          </object>
-        </child>
-      </object>
-    </child>
+  <template class="IdeEditorSurface" parent="IdeSurface">
     <child>
       <object class="GtkOverlay" id="overlay">
         <property name="visible">true</property>
@@ -30,7 +20,7 @@
               </packing>
             </child>
             <child>
-              <object class="IdeLayoutGrid" id="grid">
+              <object class="IdeGrid" id="grid">
                 <property name="orientation">horizontal</property>
                 <property name="visible">true</property>
               </object>
@@ -44,4 +34,3 @@
     </child>
   </template>
 </interface>
-
diff --git a/src/libide/editor/ide-editor-utilities.c b/src/libide/editor/ide-editor-utilities.c
index 7a1308f7b..b8fccaf26 100644
--- a/src/libide/editor/ide-editor-utilities.c
+++ b/src/libide/editor/ide-editor-utilities.c
@@ -22,7 +22,7 @@
 
 #include "config.h"
 
-#include "editor/ide-editor-utilities.h"
+#include "ide-editor-utilities.h"
 
 /**
  * SECTION:ide-editor-utilities
@@ -41,11 +41,11 @@
 
 struct _IdeEditorUtilities
 {
-  IdeLayoutPane  parent_instance;
+  IdePanel  parent_instance;
   DzlDockStack  *stack;
 };
 
-G_DEFINE_TYPE (IdeEditorUtilities, ide_editor_utilities, IDE_TYPE_LAYOUT_PANE)
+G_DEFINE_TYPE (IdeEditorUtilities, ide_editor_utilities, IDE_TYPE_PANEL)
 
 static void
 ide_editor_utilities_add (GtkContainer *container,
diff --git a/src/libide/editor/ide-editor-utilities.h b/src/libide/editor/ide-editor-utilities.h
index 992e926df..8893f35ec 100644
--- a/src/libide/editor/ide-editor-utilities.h
+++ b/src/libide/editor/ide-editor-utilities.h
@@ -20,16 +20,19 @@
 
 #pragma once
 
-#include "ide-version-macros.h"
+#if !defined (IDE_EDITOR_INSIDE) && !defined (IDE_EDITOR_COMPILATION)
+# error "Only <libide-editor.h> can be included directly."
+#endif
 
-#include "layout/ide-layout-pane.h"
+#include <libide-core.h>
+#include <libide-gui.h>
 
 G_BEGIN_DECLS
 
 #define IDE_TYPE_EDITOR_UTILITIES (ide_editor_utilities_get_type())
 
 IDE_AVAILABLE_IN_3_32
-G_DECLARE_FINAL_TYPE (IdeEditorUtilities, ide_editor_utilities, IDE, EDITOR_UTILITIES, IdeLayoutPane)
+G_DECLARE_FINAL_TYPE (IdeEditorUtilities, ide_editor_utilities, IDE, EDITOR_UTILITIES, IdePanel)
 
 /* Use GtkContainer api to add your DzlDockWidget */
 
diff --git a/src/libide/editor/ide-editor-workspace.c b/src/libide/editor/ide-editor-workspace.c
new file mode 100644
index 000000000..611dd4fa5
--- /dev/null
+++ b/src/libide/editor/ide-editor-workspace.c
@@ -0,0 +1,110 @@
+/* ide-editor-workspace.c
+ *
+ * Copyright 2018-2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-editor-workspace"
+
+#include "config.h"
+
+#include "ide-editor-surface.h"
+#include "ide-editor-workspace.h"
+
+/**
+ * SECTION:ide-editor-workspace
+ * @title: IdeEditorWorkspace
+ * @short_description: A simplified workspace for dedicated editing
+ *
+ * The #IdeEditorWorkspace is a secondary workspace that can be used to
+ * add additional #IdePage to. It does not contain the full contents of
+ * the #IdePrimaryWorkspace. It is suitable for using on an additional
+ * monitor as well as a dedicated editor in simplified Builder mode when
+ * running directly from the command line.
+ *
+ * Since: 3.32
+ */
+
+struct _IdeEditorWorkspace
+{
+  IdeWorkspace   parent_instance;
+  DzlMenuButton *surface_menu_button;
+};
+
+G_DEFINE_TYPE (IdeEditorWorkspace, ide_editor_workspace, IDE_TYPE_WORKSPACE)
+
+static void
+ide_editor_workspace_surface_set (IdeWorkspace *workspace,
+                                   IdeSurface   *surface)
+{
+  IdeEditorWorkspace *self = (IdeEditorWorkspace *)workspace;
+
+  g_assert (IDE_IS_EDITOR_WORKSPACE (self));
+  g_assert (!surface || IDE_IS_SURFACE (surface));
+
+  if (DZL_IS_DOCK_ITEM (surface))
+    {
+      g_autofree gchar *icon_name = NULL;
+
+      icon_name = dzl_dock_item_get_icon_name (DZL_DOCK_ITEM (surface));
+      g_object_set (self->surface_menu_button,
+                    "icon-name", icon_name,
+                    NULL);
+    }
+
+  IDE_WORKSPACE_CLASS (ide_editor_workspace_parent_class)->surface_set (workspace, surface);
+}
+
+static void
+ide_editor_workspace_class_init (IdeEditorWorkspaceClass *klass)
+{
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+  IdeWorkspaceClass *workspace_class = IDE_WORKSPACE_CLASS (klass);
+
+  ide_workspace_class_set_kind (workspace_class, "editor");
+
+  workspace_class->surface_set = ide_editor_workspace_surface_set;
+
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/libide-editor/ui/ide-editor-workspace.ui");
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorWorkspace, surface_menu_button);
+}
+
+static void
+ide_editor_workspace_init (IdeEditorWorkspace *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+/**
+ * ide_editor_workspace_new:
+ * @app: an #IdeApplication
+ *
+ * Creates a new #IdeEditorWorkspace.
+ *
+ * You'll need to add this to a workbench to be functional.
+ *
+ * Returns: (transfer full): an #IdeEditorWorkspace
+ *
+ * Since: 3.32
+ */
+IdeEditorWorkspace *
+ide_editor_workspace_new (IdeApplication *app)
+{
+  return g_object_new (IDE_TYPE_EDITOR_WORKSPACE,
+                       "application", app,
+                       NULL);
+}
diff --git a/src/libide/editor/ide-editor-properties.h b/src/libide/editor/ide-editor-workspace.h
similarity index 58%
rename from src/libide/editor/ide-editor-properties.h
rename to src/libide/editor/ide-editor-workspace.h
index 46352cd63..48a4472c1 100644
--- a/src/libide/editor/ide-editor-properties.h
+++ b/src/libide/editor/ide-editor-workspace.h
@@ -1,6 +1,6 @@
-/* ide-editor-properties.h
+/* ide-editor-workspace.h
  *
- * Copyright 2017-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2018-2019 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
@@ -20,18 +20,20 @@
 
 #pragma once
 
-#include <dazzle.h>
+#if !defined (IDE_EDITOR_INSIDE) && !defined (IDE_EDITOR_COMPILATION)
+# error "Only <libide-editor.h> can be included directly."
+#endif
 
-#include "editor/ide-editor-view.h"
+#include <libide-core.h>
 
 G_BEGIN_DECLS
 
-#define IDE_TYPE_EDITOR_PROPERTIES (ide_editor_properties_get_type())
+#define IDE_TYPE_EDITOR_WORKSPACE (ide_editor_workspace_get_type())
 
-G_DECLARE_FINAL_TYPE (IdeEditorProperties, ide_editor_properties, IDE, EDITOR_PROPERTIES, DzlDockWidget)
+IDE_AVAILABLE_IN_3_32
+G_DECLARE_FINAL_TYPE (IdeEditorWorkspace, ide_editor_workspace, IDE, EDITOR_WORKSPACE, IdeWorkspace)
 
-GtkWidget *ide_editor_properties_new      (void);
-void       ide_editor_properties_set_view (IdeEditorProperties *self,
-                                           IdeEditorView       *view);
+IDE_AVAILABLE_IN_3_32
+IdeEditorWorkspace *ide_editor_workspace_new (IdeApplication *app);
 
 G_END_DECLS
diff --git a/src/libide/editor/ide-editor-workspace.ui b/src/libide/editor/ide-editor-workspace.ui
new file mode 100644
index 000000000..160d93c29
--- /dev/null
+++ b/src/libide/editor/ide-editor-workspace.ui
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="IdeEditorWorkspace" parent="IdeWorkspace">
+    <child type="titlebar">
+      <object class="IdeHeaderBar">
+        <property name="show-close-button">true</property>
+        <property name="show-fullscreen-button">true</property>
+        <property name="menu-id">ide-editor-workspace-menu</property>
+        <property name="visible">true</property>
+        <child type="left">
+          <object class="IdeSurfacesButton" id="surface_menu_button">
+            <property name="focus-on-click">false</property>
+            <property name="menu-id">ide-editor-workspace-surfaces-menu</property>
+            <property name="show-accels">true</property>
+            <property name="show-arrow">true</property>
+            <property name="show-icons">true</property>
+            <!-- disable transitions since they'll cause jitter with the
+                 whole surface changing below it. -->
+            <property name="transitions-enabled">false</property>
+            <property name="has-tooltip">true</property>
+            <property name="tooltip-text" translatable="yes">Change workspace surface</property>
+          </object>
+        </child>
+        <child type="right">
+          <object class="DzlPriorityBox">
+            <property name="visible">true</property>
+            <child>
+              <object class="IdeSearchEntry" id="search_entry">
+                <property name="primary-icon-name">edit-find-symbolic</property>
+                <property name="placeholder-text" translatable="yes">Press Ctrl+. to search</property>
+                <property name="width-chars">5</property>
+                <property name="max-width-chars">24</property>
+                <property name="visible">true</property>
+              </object>
+              <packing>
+                <property name="pack-type">end</property>
+                <property name="padding">6</property>
+                <property name="priority">-100</property>
+              </packing>
+            </child>
+            <child>
+              <object class="IdeNotificationsButton" id="notifications_button">
+                <property name="visible">true</property>
+              </object>
+              <packing>
+                <property name="pack-type">end</property>
+                <property name="priority">-200</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/libide/editor/libide-editor.gresource.xml b/src/libide/editor/libide-editor.gresource.xml
new file mode 100644
index 000000000..2c390f7e5
--- /dev/null
+++ b/src/libide/editor/libide-editor.gresource.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gnome/libide-editor/ui/">
+    <file preprocess="xml-stripblanks">ide-editor-page.ui</file>
+    <file preprocess="xml-stripblanks">ide-editor-search-bar.ui</file>
+    <file preprocess="xml-stripblanks">ide-editor-settings-dialog.ui</file>
+    <file preprocess="xml-stripblanks">ide-editor-sidebar.ui</file>
+    <file preprocess="xml-stripblanks">ide-editor-surface.ui</file>
+    <file preprocess="xml-stripblanks">ide-editor-workspace.ui</file>
+  </gresource>
+</gresources>
diff --git a/src/libide/editor/libide-editor.h b/src/libide/editor/libide-editor.h
new file mode 100644
index 000000000..b541f5833
--- /dev/null
+++ b/src/libide/editor/libide-editor.h
@@ -0,0 +1,41 @@
+/* libide-editor.h
+ *
+ * Copyright 2018-2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libide-gui.h>
+#include <libide-sourceview.h>
+
+G_BEGIN_DECLS
+
+#define IDE_EDITOR_INSIDE
+
+#include "ide-editor-addin.h"
+#include "ide-editor-page.h"
+#include "ide-editor-page-addin.h"
+#include "ide-editor-search.h"
+#include "ide-editor-sidebar.h"
+#include "ide-editor-surface.h"
+#include "ide-editor-utilities.h"
+#include "ide-editor-workspace.h"
+
+#undef IDE_EDITOR_INSIDE
+
+G_END_DECLS
diff --git a/src/libide/editor/meson.build b/src/libide/editor/meson.build
index 4092e8d1a..ceaf83d3a 100644
--- a/src/libide/editor/meson.build
+++ b/src/libide/editor/meson.build
@@ -1,52 +1,121 @@
-editor_headers = [
+libide_editor_header_dir = join_paths(libide_header_dir, 'editor')
+libide_editor_header_subdir = join_paths(libide_header_subdir, 'editor')
+libide_include_directories += include_directories('.')
+
+libide_editor_sources = []
+libide_editor_public_headers = []
+libide_editor_generated_headers = []
+
+#
+# Public API Headers
+#
+
+libide_editor_public_headers = [
   'ide-editor-addin.h',
-  'ide-editor-perspective.h',
+  'ide-editor-page.h',
+  'ide-editor-page-addin.h',
   'ide-editor-search.h',
   'ide-editor-sidebar.h',
+  'ide-editor-surface.h',
   'ide-editor-utilities.h',
-  'ide-editor-view-addin.h',
-  'ide-editor-view.h',
+  'ide-editor-workspace.h',
+  'libide-editor.h',
+]
+
+libide_editor_private_headers = [
+  'ide-editor-print-operation.h',
+  'ide-editor-search-bar.h',
+  'ide-editor-settings-dialog.h',
 ]
 
-editor_sources = [
+install_headers(libide_editor_public_headers, subdir: libide_editor_header_subdir)
+
+#
+# Sources
+#
+
+libide_editor_public_sources = [
   'ide-editor-addin.c',
-  'ide-editor-perspective.c',
+  'ide-editor-page.c',
+  'ide-editor-page-addin.c',
   'ide-editor-search.c',
   'ide-editor-sidebar.c',
+  'ide-editor-surface.c',
   'ide-editor-utilities.c',
-  'ide-editor-view-addin.c',
-  'ide-editor-view.c',
+  'ide-editor-workspace.c',
 ]
 
-# .h files used for gtk-doc ignores
-editor_private_sources = [
-  'ide-editor-hover-provider.c',
-  'ide-editor-hover-provider.h',
-  'ide-editor-layout-stack-addin.c',
-  'ide-editor-layout-stack-addin.h',
-  'ide-editor-layout-stack-controls.c',
-  'ide-editor-layout-stack-controls.h',
-  'ide-editor-perspective-actions.c',
-  'ide-editor-perspective-shortcuts.c',
-  'ide-editor-plugin.c',
+
+libide_editor_private_sources = [
+  'ide-editor-page-actions.c',
+  'ide-editor-page-settings.c',
+  'ide-editor-page-shortcuts.c',
   'ide-editor-print-operation.c',
-  'ide-editor-print-operation.h',
-  'ide-editor-properties.c',
-  'ide-editor-properties.h',
   'ide-editor-search-bar.c',
-  'ide-editor-search-bar.h',
   'ide-editor-search-bar-shortcuts.c',
-  'ide-editor-session-addin.c',
-  'ide-editor-session-addin.h',
-  'ide-editor-view-actions.c',
-  'ide-editor-view-settings.c',
-  'ide-editor-view-shortcuts.c',
-  'ide-editor-workbench-addin.c',
-  'ide-editor-workbench-addin.h',
+  'ide-editor-settings-dialog.c',
+  'ide-editor-surface-actions.c',
+  'ide-editor-surface-shortcuts.c',
 ]
 
-libide_public_headers += files(editor_headers)
-libide_public_sources += files(editor_sources)
-libide_private_sources += files(editor_private_sources)
+libide_editor_sources += libide_editor_public_sources
+libide_editor_sources += libide_editor_private_sources
+
+#
+# Generated Resource Files
+#
+
+libide_editor_resources = gnome.compile_resources(
+  'ide-editor-resources',
+  'libide-editor.gresource.xml',
+  c_name: 'ide_editor',
+)
+libide_editor_generated_headers += [libide_editor_resources[1]]
+libide_editor_sources += libide_editor_resources[0]
+
+#
+# Dependencies
+#
+
+libide_editor_deps = [
+  libgio_dep,
+  libgtk_dep,
+  libdazzle_dep,
+  libpeas_dep,
+
+  libide_core_dep,
+  libide_io_dep,
+  libide_projects_dep,
+  libide_search_dep,
+  libide_sourceview_dep,
+  libide_threading_dep,
+  libide_gui_dep,
+]
+
+libide_editor_internal_deps = [
+  libpangoft2_dep,
+]
+
+#
+# Library Definitions
+#
+
+libide_editor = static_library('ide-editor-' + libide_api_version, libide_editor_sources,
+   dependencies: libide_editor_deps + libide_editor_internal_deps,
+         c_args: libide_args + release_args + ['-DIDE_EDITOR_COMPILATION'],
+)
+
+libide_editor_dep = declare_dependency(
+         dependencies: libide_editor_deps,
+           link_whole: libide_editor,
+  include_directories: include_directories('.'),
+              sources: libide_editor_generated_headers,
+)
 
-install_headers(editor_headers, subdir: join_paths(libide_header_subdir, 'editor'))
+gnome_builder_public_sources += files(libide_editor_public_sources)
+gnome_builder_public_headers += files(libide_editor_public_headers)
+gnome_builder_private_sources += files(libide_editor_private_sources)
+gnome_builder_private_headers += files(libide_editor_private_headers)
+gnome_builder_generated_headers += libide_editor_generated_headers
+gnome_builder_include_subdirs += libide_editor_header_subdir
+gnome_builder_gir_extra_args += ['--c-include=libide-editor.h', '-DIDE_EDITOR_COMPILATION']
diff --git a/src/plugins/editor/default.css b/src/plugins/editor/default.css
new file mode 100644
index 000000000..e1b0177a1
--- /dev/null
+++ b/src/plugins/editor/default.css
@@ -0,0 +1,60 @@
+@import url("resource:///org/gnome/builder/keybindings/shared.css");
+
+@binding-set default-ide-source-view
+{
+  bind "Escape" { "clear-search" ()
+                  "clear-modifier" ()
+                  "clear-selection" ()
+                  "clear-count" ()
+                  "clear-snippets" ()
+                  "hide-completion" ()
+                  "remove-cursors" () };
+  bind "<ctrl><shift>e" { "add-cursor" (column) };
+  bind "<ctrl><shift>d" { "add-cursor" (match) };
+  bind "<alt>period" { "goto-definition" () };
+  bind "<ctrl>k" { "action" ("frame", "show-list", "") };
+  bind "<ctrl>d" { "delete-from-cursor" (paragraphs, 1) };
+  bind "<ctrl>j" { "join-lines" () };
+  bind "<ctrl>u" { "change-case" (upper) };
+  bind "<ctrl>l" { "change-case" (lower) };
+  bind "<ctrl>i" { "action" ("editor-page", "goto-line", "") };
+  bind "<ctrl>asciitilde" { "change-case" (toggle) };
+  bind "<ctrl><alt>d" { "duplicate-entire-line" ()};
+  bind "<ctrl><shift>z" { "clear-count" ()
+                          "clear-selection" ()
+                          "remove-cursors" ()
+                          "redo" () };
+  bind "<ctrl>z" { "clear-count" ()
+                   "clear-selection" ()
+                   "remove-cursors" ()
+                   "undo" () };
+
+  bind "<ctrl>minus" { "decrease-font-size" () };
+  bind "<ctrl>plus" { "increase-font-size" () };
+  bind "<ctrl>equal" { "increase-font-size" () };
+  bind "<ctrl>0" { "reset-font-size" () };
+
+  /* cycle "tabs" */
+  bind "<ctrl><alt>Page_Up" { "action" ("frame", "previous-page", "") };
+  bind "<ctrl><alt>KP_Page_Up" { "action" ("frame", "previous-page", "") };
+  bind "<ctrl><alt>Page_Down" { "action" ("frame", "next-page", "") };
+  bind "<ctrl><alt>KP_Page_Down" { "action" ("frame", "next-page", "") };
+
+  bind "F2" { "clear-selection" ()
+              "movement" (previous-word-end, 0, 1, 1)
+              "movement" (next-word-start, 0, 1, 0)
+              "movement" (next-word-end, 1, 0, 1)
+              "request-documentation" ()
+              "clear-count" ()
+              "clear-selection" () };
+  bind "F4" { "action" ("win", "find-other-file", "") };
+
+  bind "<ctrl><alt>i" { "reindent" () };
+
+  /* Add back emoji */
+  bind "<ctrl>semicolon" { "insert-emoji" () };
+}
+
+idesourceviewmode.default {
+  -gtk-key-bindings: default-ide-source-view;
+}
diff --git a/src/plugins/editor/editor-plugin.c b/src/plugins/editor/editor-plugin.c
new file mode 100644
index 000000000..6c266fea4
--- /dev/null
+++ b/src/plugins/editor/editor-plugin.c
@@ -0,0 +1,56 @@
+/* editor-plugin.c
+ *
+ * Copyright 2018-2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "editor-plugin"
+
+#include "config.h"
+
+#include <libpeas/peas.h>
+#include <libide-editor.h>
+
+#include "gbp-editor-application-addin.h"
+#include "gbp-editor-frame-addin.h"
+#include "gbp-editor-hover-provider.h"
+#include "gbp-editor-session-addin.h"
+#include "gbp-editor-workbench-addin.h"
+#include "gbp-editor-workspace-addin.h"
+
+_IDE_EXTERN void
+_gbp_editor_register_types (PeasObjectModule *module)
+{
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_APPLICATION_ADDIN,
+                                              GBP_TYPE_EDITOR_APPLICATION_ADDIN);
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_HOVER_PROVIDER,
+                                              GBP_TYPE_EDITOR_HOVER_PROVIDER);
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_FRAME_ADDIN,
+                                              GBP_TYPE_EDITOR_FRAME_ADDIN);
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_SESSION_ADDIN,
+                                              GBP_TYPE_EDITOR_SESSION_ADDIN);
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_WORKBENCH_ADDIN,
+                                              GBP_TYPE_EDITOR_WORKBENCH_ADDIN);
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_WORKSPACE_ADDIN,
+                                              GBP_TYPE_EDITOR_WORKSPACE_ADDIN);
+}
diff --git a/src/plugins/editor/editor.gresource.xml b/src/plugins/editor/editor.gresource.xml
new file mode 100644
index 000000000..8f9624a5b
--- /dev/null
+++ b/src/plugins/editor/editor.gresource.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/plugins/editor">
+    <file>gtk/menus.ui</file>
+    <file>editor.plugin</file>
+    <file>gbp-editor-frame-controls.ui</file>
+  </gresource>
+  <gresource prefix="/org/gnome/builder/keybindings">
+    <file>default.css</file>
+    <file>shared.css</file>
+  </gresource>
+</gresources>
diff --git a/src/plugins/editor/editor.plugin b/src/plugins/editor/editor.plugin
new file mode 100644
index 000000000..f534c283b
--- /dev/null
+++ b/src/plugins/editor/editor.plugin
@@ -0,0 +1,11 @@
+[Plugin]
+Authors=Christian Hergert <christian hergert me>
+Builtin=true
+Copyright=Copyright © 2014-2018 Christian Hergert
+Description=Builder's greeter window
+Embedded=_gbp_editor_register_types
+Hidden=true
+Module=editor
+Name=Editor
+X-At-Startup=true
+X-Workspace-Kind=primary;editor;
diff --git a/src/plugins/editor/gbp-editor-application-addin.c 
b/src/plugins/editor/gbp-editor-application-addin.c
new file mode 100644
index 000000000..7a1fff061
--- /dev/null
+++ b/src/plugins/editor/gbp-editor-application-addin.c
@@ -0,0 +1,199 @@
+/* gbp-editor-application-addin.c
+ *
+ * Copyright 2018-2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-editor-application-addin"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <libide-editor.h>
+#include <libide-gui.h>
+#include <stdlib.h>
+
+#include "ide-window-settings-private.h"
+
+#include "gbp-editor-application-addin.h"
+
+struct _GbpEditorApplicationAddin
+{
+  GObject parent_instance;
+};
+
+static GFile *
+get_common_ancestor (GPtrArray *files)
+{
+  GFile *ancestor;
+
+  if (files->len == 0)
+    return NULL;
+
+  ancestor = g_file_get_parent (g_ptr_array_index (files, 0));
+
+  for (guint i = 1; i < files->len; i++)
+    {
+      GFile *file = g_ptr_array_index (files, i);
+
+      while (!g_file_has_prefix (file, ancestor))
+        {
+          GFile *old = ancestor;
+          ancestor = g_file_get_parent (old);
+          if (g_file_equal (ancestor, old))
+            break;
+          g_object_unref (old);
+        }
+    }
+
+  return g_steal_pointer (&ancestor);
+}
+
+static void
+gbp_editor_application_addin_add_option_entries (IdeApplicationAddin *addin,
+                                                 IdeApplication      *app)
+{
+  g_assert (IDE_IS_APPLICATION_ADDIN (addin));
+  g_assert (G_IS_APPLICATION (app));
+
+  g_application_add_main_option (G_APPLICATION (app),
+                                 "editor",
+                                 'e',
+                                 G_OPTION_FLAG_IN_MAIN,
+                                 G_OPTION_ARG_NONE,
+                                 _("Use minial editor interface"),
+                                 NULL);
+}
+
+static void
+gbp_editor_application_addin_open_all_cb (GObject      *object,
+                                          GAsyncResult *result,
+                                          gpointer      user_data)
+{
+  IdeWorkbench *workbench = (IdeWorkbench *)object;
+  g_autoptr(GApplicationCommandLine) cmdline = user_data;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (IDE_IS_WORKBENCH (workbench));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (G_IS_APPLICATION_COMMAND_LINE (cmdline));
+
+  if (!ide_workbench_open_finish (workbench, result, &error))
+    {
+      g_application_command_line_printerr (cmdline, "%s\n", error->message);
+      g_application_command_line_set_exit_status (cmdline, EXIT_FAILURE);
+      return;
+    }
+
+  g_application_command_line_set_exit_status (cmdline, EXIT_SUCCESS);
+}
+
+static void
+gbp_editor_application_addin_handle_command_line (IdeApplicationAddin     *addin,
+                                                  IdeApplication          *application,
+                                                  GApplicationCommandLine *cmdline)
+{
+  g_autoptr(IdeWorkbench) workbench = NULL;
+  IdeEditorWorkspace *workspace;
+  IdeApplication *app = (IdeApplication *)application;
+  g_autoptr(GPtrArray) files = NULL;
+  g_autoptr(GFile) workdir = NULL;
+  g_auto(GStrv) argv = NULL;
+  GVariantDict *options;
+  IdeContext *context;
+  gint argc;
+
+  g_assert (IDE_IS_APPLICATION_ADDIN (addin));
+  g_assert (IDE_IS_APPLICATION (app));
+  g_assert (G_IS_APPLICATION_COMMAND_LINE (cmdline));
+
+  if ((options = g_application_command_line_get_options_dict (cmdline)) &&
+      g_variant_dict_contains (options, "editor"))
+    ide_application_set_workspace_type (application, IDE_TYPE_EDITOR_WORKSPACE);
+
+  /* Ignore if no parameters were passed */
+  argv = g_application_command_line_get_arguments (cmdline, &argc);
+  if (argc < 2)
+    return;
+
+  /*
+   * If the user is trying to open various files using the command line with
+   * something like "gnome-builder x.c y.c z.c" then instead of opening the
+   * full project system, we'll open a simplified editor workspace for just
+   * these files and avoid loading a project altogether. That means that they
+   * wont get all of the IDE experience, but its faster to get quick editing
+   * done and then exit.
+   */
+
+  files = g_ptr_array_new_with_free_func (g_object_unref);
+  for (guint i = 1; i < argc; i++)
+    g_ptr_array_add (files,
+                     g_application_command_line_create_file_for_arg (cmdline, argv[i]));
+
+  workbench = ide_workbench_new ();
+  ide_application_add_workbench (app, workbench);
+
+  workdir = get_common_ancestor (files);
+  context = ide_workbench_get_context (workbench);
+
+  /* Setup the working directory to top-most common ancestor of the
+   * files. That way we can still get somewhat localized search results
+   * and other workspace features.
+   */
+  if (workdir != NULL)
+    ide_context_set_workdir (context, workdir);
+
+  workspace = ide_editor_workspace_new (app);
+  ide_workbench_add_workspace (workbench, IDE_WORKSPACE (workspace));
+
+  /* Since we are opening a toplevel window, we want to restore it using
+   * the same window sizing as the primary IDE window.
+   */
+  _ide_window_settings_register (GTK_WINDOW (workspace));
+
+  ide_workbench_focus_workspace (workbench, IDE_WORKSPACE (workspace));
+
+  g_assert (files->len > 0);
+
+  ide_workbench_open_all_async (workbench,
+                                (GFile **)(gpointer)files->pdata,
+                                files->len,
+                                "editor",
+                                NULL,
+                                gbp_editor_application_addin_open_all_cb,
+                                g_object_ref (cmdline));
+}
+
+static void
+cmdline_addin_iface_init (IdeApplicationAddinInterface *iface)
+{
+  iface->add_option_entries = gbp_editor_application_addin_add_option_entries;
+  iface->handle_command_line = gbp_editor_application_addin_handle_command_line;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GbpEditorApplicationAddin, gbp_editor_application_addin, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (IDE_TYPE_APPLICATION_ADDIN, cmdline_addin_iface_init))
+
+static void
+gbp_editor_application_addin_class_init (GbpEditorApplicationAddinClass *klass)
+{
+}
+
+static void
+gbp_editor_application_addin_init (GbpEditorApplicationAddin *self)
+{
+}
diff --git a/src/plugins/editor/gbp-editor-application-addin.h 
b/src/plugins/editor/gbp-editor-application-addin.h
new file mode 100644
index 000000000..7549490a4
--- /dev/null
+++ b/src/plugins/editor/gbp-editor-application-addin.h
@@ -0,0 +1,31 @@
+/* gbp-editor-application-addin.h
+ *
+ * Copyright 2018-2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libide-core.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_EDITOR_APPLICATION_ADDIN (gbp_editor_application_addin_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpEditorApplicationAddin, gbp_editor_application_addin, GBP, 
EDITOR_APPLICATION_ADDIN, GObject)
+
+G_END_DECLS
diff --git a/src/plugins/editor/gbp-editor-frame-addin.c b/src/plugins/editor/gbp-editor-frame-addin.c
new file mode 100644
index 000000000..d9c339603
--- /dev/null
+++ b/src/plugins/editor/gbp-editor-frame-addin.c
@@ -0,0 +1,115 @@
+/* gbp-editor-frame-addin.c
+ *
+ * Copyright 2017-2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-editor-frame-addin.h"
+
+#include "config.h"
+
+#include <libide-gui.h>
+#include <libide-editor.h>
+
+#include "gbp-editor-frame-addin.h"
+#include "gbp-editor-frame-controls.h"
+
+struct _GbpEditorFrameAddin
+{
+  GObject                 parent_instance;
+  GbpEditorFrameControls *controls;
+};
+
+static void
+gbp_editor_frame_addin_load (IdeFrameAddin *addin,
+                             IdeFrame      *stack)
+{
+  GbpEditorFrameAddin *self = (GbpEditorFrameAddin *)addin;
+  GtkWidget *header;
+
+  g_assert (GBP_IS_EDITOR_FRAME_ADDIN (self));
+  g_assert (IDE_IS_FRAME (stack));
+
+  header = ide_frame_get_titlebar (stack);
+
+  self->controls = g_object_new (GBP_TYPE_EDITOR_FRAME_CONTROLS, NULL);
+  g_signal_connect (self->controls,
+                    "destroy",
+                    G_CALLBACK (gtk_widget_destroyed),
+                    &self->controls);
+  gtk_container_add_with_properties (GTK_CONTAINER (header), GTK_WIDGET (self->controls),
+                                     "pack-type", GTK_PACK_END,
+                                     "priority", 100,
+                                     NULL);
+}
+
+static void
+gbp_editor_frame_addin_unload (IdeFrameAddin *addin,
+                               IdeFrame      *stack)
+{
+  GbpEditorFrameAddin *self = (GbpEditorFrameAddin *)addin;
+
+  g_assert (GBP_IS_EDITOR_FRAME_ADDIN (self));
+  g_assert (IDE_IS_FRAME (stack));
+
+  if (self->controls != NULL)
+    gtk_widget_destroy (GTK_WIDGET (self->controls));
+}
+
+static void
+gbp_editor_frame_addin_set_page (IdeFrameAddin *addin,
+                                 IdePage       *page)
+{
+  GbpEditorFrameAddin *self = (GbpEditorFrameAddin *)addin;
+
+  g_assert (GBP_IS_EDITOR_FRAME_ADDIN (self));
+  g_assert (!page || IDE_IS_PAGE (page));
+
+  if (IDE_IS_EDITOR_PAGE (page))
+    {
+      gbp_editor_frame_controls_set_page (self->controls, IDE_EDITOR_PAGE (page));
+      gtk_widget_show (GTK_WIDGET (self->controls));
+    }
+  else
+    {
+      gbp_editor_frame_controls_set_page (self->controls, NULL);
+      gtk_widget_hide (GTK_WIDGET (self->controls));
+    }
+}
+
+static void
+frame_addin_iface_init (IdeFrameAddinInterface *iface)
+{
+  iface->load = gbp_editor_frame_addin_load;
+  iface->unload = gbp_editor_frame_addin_unload;
+  iface->set_page = gbp_editor_frame_addin_set_page;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GbpEditorFrameAddin,
+                         gbp_editor_frame_addin,
+                         G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (IDE_TYPE_FRAME_ADDIN, frame_addin_iface_init))
+
+static void
+gbp_editor_frame_addin_class_init (GbpEditorFrameAddinClass *klass)
+{
+}
+
+static void
+gbp_editor_frame_addin_init (GbpEditorFrameAddin *self)
+{
+}
diff --git a/src/libide/editor/ide-editor-layout-stack-addin.h b/src/plugins/editor/gbp-editor-frame-addin.h
similarity index 74%
rename from src/libide/editor/ide-editor-layout-stack-addin.h
rename to src/plugins/editor/gbp-editor-frame-addin.h
index be0e297cc..9acaf2d3c 100644
--- a/src/libide/editor/ide-editor-layout-stack-addin.h
+++ b/src/plugins/editor/gbp-editor-frame-addin.h
@@ -1,4 +1,4 @@
-/* ide-editor-layout-stack-addin.h
+/* gbp-editor-frame-addin.h
  *
  * Copyright 2017-2019 Christian Hergert <chergert redhat com>
  *
@@ -20,12 +20,12 @@
 
 #pragma once
 
-#include "layout/ide-layout-stack-addin.h"
+#include <glib-object.h>
 
 G_BEGIN_DECLS
 
-#define IDE_TYPE_EDITOR_LAYOUT_STACK_ADDIN (ide_editor_layout_stack_addin_get_type())
+#define GBP_TYPE_EDITOR_FRAME_ADDIN (gbp_editor_frame_addin_get_type())
 
-G_DECLARE_FINAL_TYPE (IdeEditorLayoutStackAddin, ide_editor_layout_stack_addin, IDE, 
EDITOR_LAYOUT_STACK_ADDIN, GObject)
+G_DECLARE_FINAL_TYPE (GbpEditorFrameAddin, gbp_editor_frame_addin, GBP, EDITOR_FRAME_ADDIN, GObject)
 
 G_END_DECLS
diff --git a/src/libide/editor/ide-editor-layout-stack-controls.c 
b/src/plugins/editor/gbp-editor-frame-controls.c
similarity index 60%
rename from src/libide/editor/ide-editor-layout-stack-controls.c
rename to src/plugins/editor/gbp-editor-frame-controls.c
index 85aaaf8c0..15802d3cd 100644
--- a/src/libide/editor/ide-editor-layout-stack-controls.c
+++ b/src/plugins/editor/gbp-editor-frame-controls.c
@@ -1,4 +1,4 @@
-/* ide-editor-layout-stack-controls.c
+/* gbp-editor-frame-controls.c
  *
  * Copyright 2016-2019 Christian Hergert <chergert redhat com>
  *
@@ -18,22 +18,26 @@
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
 
-#define G_LOG_DOMAIN "ide-editor-layout-stack-controls"
+#define G_LOG_DOMAIN "gbp-editor-frame-controls"
 
 #include "config.h"
 
 #include <glib/gi18n.h>
+#include <libide-editor.h>
 
-#include "buffers/ide-buffer-private.h"
-#include "editor/ide-editor-layout-stack-controls.h"
-#include "editor/ide-editor-private.h"
+#define IDE_EDITOR_INSIDE
+#include "ide-editor-private.h"
+#undef IDE_EDITOR_INSIDE
 
-G_DEFINE_TYPE (IdeEditorLayoutStackControls, ide_editor_layout_stack_controls, GTK_TYPE_BOX)
+#include "gbp-editor-frame-controls.h"
+
+
+G_DEFINE_TYPE (GbpEditorFrameControls, gbp_editor_frame_controls, GTK_TYPE_BOX)
 
 static void
-document_cursor_moved (IdeEditorLayoutStackControls *self,
-                       const GtkTextIter            *iter,
-                       GtkTextBuffer                *buffer)
+document_cursor_moved (GbpEditorFrameControls *self,
+                       const GtkTextIter      *iter,
+                       GtkTextBuffer          *buffer)
 {
   IdeSourceView *source_view;
   GtkTextIter bounds;
@@ -43,17 +47,17 @@ document_cursor_moved (IdeEditorLayoutStackControls *self,
   gint column;
   gint column2;
 
-  g_assert (IDE_IS_EDITOR_LAYOUT_STACK_CONTROLS (self));
+  g_assert (GBP_IS_EDITOR_FRAME_CONTROLS (self));
   g_assert (iter != NULL);
   g_assert (IDE_IS_BUFFER (buffer));
 
-  if (self->view == NULL)
+  if (self->page == NULL)
     return;
 
   if (ide_buffer_get_loading (IDE_BUFFER (buffer)))
     return;
 
-  source_view = ide_editor_view_get_view (self->view);
+  source_view = ide_editor_page_get_view (self->page);
 
   ide_source_view_get_visual_position (source_view, &line, (guint *)&column);
 
@@ -86,16 +90,16 @@ document_cursor_moved (IdeEditorLayoutStackControls *self,
 
 
 static void
-goto_line_activate (IdeEditorLayoutStackControls *self,
-                    const gchar                  *text,
-                    DzlSimplePopover             *popover)
+goto_line_activate (GbpEditorFrameControls *self,
+                    const gchar            *text,
+                    DzlSimplePopover       *popover)
 {
   gint64 value;
 
-  g_assert (IDE_IS_EDITOR_LAYOUT_STACK_CONTROLS (self));
+  g_assert (GBP_IS_EDITOR_FRAME_CONTROLS (self));
   g_assert (DZL_IS_SIMPLE_POPOVER (popover));
 
-  if (self->view == NULL)
+  if (self->page == NULL)
     return;
 
   if (!dzl_str_empty0 (text))
@@ -105,12 +109,12 @@ goto_line_activate (IdeEditorLayoutStackControls *self,
       if ((value > 0) && (value < G_MAXINT))
         {
           IdeSourceView *source_view;
-          GtkTextBuffer *buffer = GTK_TEXT_BUFFER (self->view->buffer);
+          GtkTextBuffer *buffer = GTK_TEXT_BUFFER (self->page->buffer);
           GtkTextIter iter;
 
-          source_view = ide_editor_view_get_view (self->view);
+          source_view = ide_editor_page_get_view (self->page);
 
-          gtk_widget_grab_focus (GTK_WIDGET (self->view));
+          gtk_widget_grab_focus (GTK_WIDGET (self->page));
           gtk_text_buffer_get_iter_at_line (buffer, &iter, value - 1);
           gtk_text_buffer_select_range (buffer, &iter, &iter);
           ide_source_view_scroll_to_iter (source_view, &iter, 0.25, TRUE, 1.0, 0.5, TRUE);
@@ -119,13 +123,13 @@ goto_line_activate (IdeEditorLayoutStackControls *self,
 }
 
 static gboolean
-goto_line_insert_text (IdeEditorLayoutStackControls *self,
-                       guint                         position,
-                       const gchar                  *chars,
-                       guint                         n_chars,
-                       DzlSimplePopover             *popover)
+goto_line_insert_text (GbpEditorFrameControls *self,
+                       guint                   position,
+                       const gchar            *chars,
+                       guint                   n_chars,
+                       DzlSimplePopover       *popover)
 {
-  g_assert (IDE_IS_EDITOR_LAYOUT_STACK_CONTROLS (self));
+  g_assert (GBP_IS_EDITOR_FRAME_CONTROLS (self));
   g_assert (DZL_IS_SIMPLE_POPOVER (popover));
   g_assert (chars != NULL);
 
@@ -139,23 +143,23 @@ goto_line_insert_text (IdeEditorLayoutStackControls *self,
 }
 
 static void
-goto_line_changed (IdeEditorLayoutStackControls *self,
-                   DzlSimplePopover             *popover)
+goto_line_changed (GbpEditorFrameControls *self,
+                   DzlSimplePopover       *popover)
 {
   gchar *message;
   const gchar *text;
   GtkTextIter begin;
   GtkTextIter end;
 
-  g_assert (IDE_IS_EDITOR_LAYOUT_STACK_CONTROLS (self));
+  g_assert (GBP_IS_EDITOR_FRAME_CONTROLS (self));
   g_assert (DZL_IS_SIMPLE_POPOVER (popover));
 
-  if (self->view == NULL)
+  if (self->page == NULL)
     return;
 
   text = dzl_simple_popover_get_text (popover);
 
-  gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (self->view->buffer), &begin, &end);
+  gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (self->page->buffer), &begin, &end);
 
   if (!dzl_str_empty0 (text))
     {
@@ -184,41 +188,41 @@ goto_line_changed (IdeEditorLayoutStackControls *self,
 }
 
 static void
-warning_button_clicked (IdeEditorLayoutStackControls *self,
-                        GtkButton                    *button)
+warning_button_clicked (GbpEditorFrameControls *self,
+                        GtkButton              *button)
 {
   IdeSourceView *source_view;
 
-  g_assert (IDE_IS_EDITOR_LAYOUT_STACK_CONTROLS (self));
+  g_assert (GBP_IS_EDITOR_FRAME_CONTROLS (self));
   g_assert (GTK_IS_BUTTON (button));
 
-  if (self->view == NULL)
+  if (self->page == NULL)
     return;
 
-  source_view = ide_editor_view_get_view (self->view);
+  source_view = ide_editor_page_get_view (self->page);
   gtk_widget_grab_focus (GTK_WIDGET (source_view));
   g_signal_emit_by_name (source_view, "move-error", GTK_DIR_DOWN);
 }
 
 static void
-show_goto_line (GSimpleAction                 *action,
-                GVariant                      *param,
-                IdeEditorLayoutStackControls  *self)
+show_goto_line (GSimpleAction          *action,
+                GVariant               *param,
+                GbpEditorFrameControls *self)
 {
   g_assert (G_IS_SIMPLE_ACTION (action));
-  g_assert (IDE_IS_EDITOR_LAYOUT_STACK_CONTROLS (self));
+  g_assert (GBP_IS_EDITOR_FRAME_CONTROLS (self));
 
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->goto_line_button), TRUE);
 }
 
 static void
-ide_editor_layout_stack_controls_bind (IdeEditorLayoutStackControls *self,
-                                       GtkTextBuffer                *buffer,
-                                       DzlSignalGroup               *buffer_signals)
+gbp_editor_frame_controls_bind (GbpEditorFrameControls *self,
+                                GtkTextBuffer          *buffer,
+                                DzlSignalGroup         *buffer_signals)
 {
   GtkTextIter iter;
 
-  g_assert (IDE_IS_EDITOR_LAYOUT_STACK_CONTROLS (self));
+  g_assert (GBP_IS_EDITOR_FRAME_CONTROLS (self));
   g_assert (IDE_IS_BUFFER (buffer));
   g_assert (DZL_IS_SIGNAL_GROUP (buffer_signals));
 
@@ -227,38 +231,38 @@ ide_editor_layout_stack_controls_bind (IdeEditorLayoutStackControls *self,
 }
 
 static void
-ide_editor_layout_stack_controls_finalize (GObject *object)
+gbp_editor_frame_controls_finalize (GObject *object)
 {
-  IdeEditorLayoutStackControls *self = (IdeEditorLayoutStackControls *)object;
+  GbpEditorFrameControls *self = (GbpEditorFrameControls *)object;
 
   g_clear_object (&self->buffer_bindings);
   g_clear_object (&self->buffer_signals);
   g_clear_object (&self->goto_line_action);
 
-  self->view = NULL;
+  self->page = NULL;
 
-  G_OBJECT_CLASS (ide_editor_layout_stack_controls_parent_class)->finalize (object);
+  G_OBJECT_CLASS (gbp_editor_frame_controls_parent_class)->finalize (object);
 }
 
 static void
-ide_editor_layout_stack_controls_class_init (IdeEditorLayoutStackControlsClass *klass)
+gbp_editor_frame_controls_class_init (GbpEditorFrameControlsClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
-  object_class->finalize = ide_editor_layout_stack_controls_finalize;
+  object_class->finalize = gbp_editor_frame_controls_finalize;
 
-  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/builder/ui/ide-editor-layout-stack-controls.ui");
-  gtk_widget_class_bind_template_child (widget_class, IdeEditorLayoutStackControls, column_label);
-  gtk_widget_class_bind_template_child (widget_class, IdeEditorLayoutStackControls, goto_line_popover);
-  gtk_widget_class_bind_template_child (widget_class, IdeEditorLayoutStackControls, goto_line_button);
-  gtk_widget_class_bind_template_child (widget_class, IdeEditorLayoutStackControls, line_label);
-  gtk_widget_class_bind_template_child (widget_class, IdeEditorLayoutStackControls, range_label);
-  gtk_widget_class_bind_template_child (widget_class, IdeEditorLayoutStackControls, warning_button);
+  gtk_widget_class_set_template_from_resource (widget_class, "/plugins/editor/gbp-editor-frame-controls.ui");
+  gtk_widget_class_bind_template_child (widget_class, GbpEditorFrameControls, column_label);
+  gtk_widget_class_bind_template_child (widget_class, GbpEditorFrameControls, goto_line_popover);
+  gtk_widget_class_bind_template_child (widget_class, GbpEditorFrameControls, goto_line_button);
+  gtk_widget_class_bind_template_child (widget_class, GbpEditorFrameControls, line_label);
+  gtk_widget_class_bind_template_child (widget_class, GbpEditorFrameControls, range_label);
+  gtk_widget_class_bind_template_child (widget_class, GbpEditorFrameControls, warning_button);
 }
 
 static void
-ide_editor_layout_stack_controls_init (IdeEditorLayoutStackControls *self)
+gbp_editor_frame_controls_init (GbpEditorFrameControls *self)
 {
   gtk_widget_init_template (GTK_WIDGET (self));
 
@@ -296,7 +300,7 @@ ide_editor_layout_stack_controls_init (IdeEditorLayoutStackControls *self)
 
   g_signal_connect_swapped (self->buffer_signals,
                             "bind",
-                            G_CALLBACK (ide_editor_layout_stack_controls_bind),
+                            G_CALLBACK (gbp_editor_frame_controls_bind),
                             self);
 
   dzl_signal_group_connect_object (self->buffer_signals,
@@ -314,39 +318,39 @@ ide_editor_layout_stack_controls_init (IdeEditorLayoutStackControls *self)
 }
 
 void
-ide_editor_layout_stack_controls_set_view (IdeEditorLayoutStackControls *self,
-                                           IdeEditorView                *view)
+gbp_editor_frame_controls_set_page (GbpEditorFrameControls *self,
+                                    IdeEditorPage          *page)
 {
-  GActionGroup *editor_view_group;
+  GActionGroup *editor_page_group;
 
-  g_return_if_fail (IDE_IS_EDITOR_LAYOUT_STACK_CONTROLS (self));
-  g_return_if_fail (!view || IDE_IS_EDITOR_VIEW (view));
+  g_return_if_fail (GBP_IS_EDITOR_FRAME_CONTROLS (self));
+  g_return_if_fail (!page || IDE_IS_EDITOR_PAGE (page));
 
-  if (self->view == view)
+  if (self->page == page)
     return;
 
   dzl_binding_group_set_source (self->buffer_bindings, NULL);
   dzl_signal_group_set_target (self->buffer_signals, NULL);
 
-  if (self->view != NULL)
+  if (self->page != NULL)
     {
-      g_signal_handlers_disconnect_by_func (self->view,
+      g_signal_handlers_disconnect_by_func (self->page,
                                             G_CALLBACK (gtk_widget_destroyed),
-                                            &self->view);
-      self->view = NULL;
+                                            &self->page);
+      self->page = NULL;
     }
 
-  if (view != NULL)
+  if (page != NULL)
     {
-      self->view = view;
-      g_signal_connect (view,
+      self->page = page;
+      g_signal_connect (page,
                         "destroy",
                         G_CALLBACK (gtk_widget_destroyed),
-                        &self->view);
-      dzl_binding_group_set_source (self->buffer_bindings, view->buffer);
-      dzl_signal_group_set_target (self->buffer_signals, view->buffer);
+                        &self->page);
+      dzl_binding_group_set_source (self->buffer_bindings, page->buffer);
+      dzl_signal_group_set_target (self->buffer_signals, page->buffer);
 
-      if (NULL != (editor_view_group = gtk_widget_get_action_group (GTK_WIDGET (view), "editor-view")))
-        g_action_map_add_action (G_ACTION_MAP (editor_view_group), G_ACTION (self->goto_line_action));
+      if (NULL != (editor_page_group = gtk_widget_get_action_group (GTK_WIDGET (page), "editor-page")))
+        g_action_map_add_action (G_ACTION_MAP (editor_page_group), G_ACTION (self->goto_line_action));
     }
 }
diff --git a/src/libide/editor/ide-editor-layout-stack-controls.h 
b/src/plugins/editor/gbp-editor-frame-controls.h
similarity index 70%
rename from src/libide/editor/ide-editor-layout-stack-controls.h
rename to src/plugins/editor/gbp-editor-frame-controls.h
index b98a13e05..c9163f2e7 100644
--- a/src/libide/editor/ide-editor-layout-stack-controls.h
+++ b/src/plugins/editor/gbp-editor-frame-controls.h
@@ -1,4 +1,4 @@
-/* ide-editor-layout-stack-controls.h
+/* gbp-editor-frame-controls.h
  *
  * Copyright 2016-2019 Christian Hergert <chergert redhat com>
  *
@@ -23,19 +23,21 @@
 #include <gtk/gtk.h>
 #include <dazzle.h>
 
-#include "editor/ide-editor-view.h"
+#include <gtk/gtk.h>
+#include <dazzle.h>
+#include <libide-editor.h>
 
 G_BEGIN_DECLS
 
-#define IDE_TYPE_EDITOR_LAYOUT_STACK_CONTROLS (ide_editor_layout_stack_controls_get_type())
+#define GBP_TYPE_EDITOR_FRAME_CONTROLS (gbp_editor_frame_controls_get_type())
 
-G_DECLARE_FINAL_TYPE (IdeEditorLayoutStackControls, ide_editor_layout_stack_controls, IDE, 
EDITOR_LAYOUT_STACK_CONTROLS, GtkBox)
+G_DECLARE_FINAL_TYPE (GbpEditorFrameControls, gbp_editor_frame_controls, GBP, EDITOR_FRAME_CONTROLS, GtkBox)
 
-struct _IdeEditorLayoutStackControls
+struct _GbpEditorFrameControls
 {
   GtkBox                parent_instance;
 
-  IdeEditorView        *view;
+  IdeEditorPage        *page;
   DzlBindingGroup      *buffer_bindings;
   DzlSignalGroup       *buffer_signals;
 
@@ -49,7 +51,7 @@ struct _IdeEditorLayoutStackControls
   GSimpleAction        *goto_line_action;
 };
 
-void ide_editor_layout_stack_controls_set_view (IdeEditorLayoutStackControls *self,
-                                                IdeEditorView                *view);
+void gbp_editor_frame_controls_set_page (GbpEditorFrameControls *self,
+                                         IdeEditorPage          *page);
 
 G_END_DECLS
diff --git a/src/libide/editor/ide-editor-layout-stack-controls.ui 
b/src/plugins/editor/gbp-editor-frame-controls.ui
similarity index 97%
rename from src/libide/editor/ide-editor-layout-stack-controls.ui
rename to src/plugins/editor/gbp-editor-frame-controls.ui
index 1dcc2eb1c..d3aa8bc13 100644
--- a/src/libide/editor/ide-editor-layout-stack-controls.ui
+++ b/src/plugins/editor/gbp-editor-frame-controls.ui
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <template class="IdeEditorLayoutStackControls" parent="GtkBox">
+  <template class="GbpEditorFrameControls" parent="GtkBox">
     <property name="orientation">horizontal</property>
     <child>
       <object class="GtkButton" id="warning_button">
diff --git a/src/libide/editor/ide-editor-hover-provider.c b/src/plugins/editor/gbp-editor-hover-provider.c
similarity index 71%
rename from src/libide/editor/ide-editor-hover-provider.c
rename to src/plugins/editor/gbp-editor-hover-provider.c
index 1d4dc6743..b4319775d 100644
--- a/src/libide/editor/ide-editor-hover-provider.c
+++ b/src/plugins/editor/gbp-editor-hover-provider.c
@@ -1,4 +1,4 @@
-/* ide-editor-hover-provider.c
+/* gbp-editor-hover-provider.c
  *
  * Copyright 2018-2019 Christian Hergert <chergert redhat com>
  *
@@ -18,52 +18,54 @@
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
 
-#define G_LOG_DOMAIN "ide-editor-hover-provider"
+#define G_LOG_DOMAIN "gbp-editor-hover-provider"
 
 #include "config.h"
 
 #include <glib/gi18n.h>
+#include <libide-code.h>
+#include <libide-sourceview.h>
+#include <libide-threading.h>
 
-#include "buffers/ide-buffer.h"
-#include "diagnostics/ide-diagnostic.h"
-#include "editor/ide-editor-hover-provider.h"
-#include "threading/ide-task.h"
+#include "gbp-editor-hover-provider.h"
 
 #define DIAGNOSTICS_HOVER_PRIORITY 500
 
-struct _IdeEditorHoverProvider
+struct _GbpEditorHoverProvider
 {
   GObject parent_instance;
 };
 
 static void
-ide_editor_hover_provider_hover_async (IdeHoverProvider    *provider,
+gbp_editor_hover_provider_hover_async (IdeHoverProvider    *provider,
                                        IdeHoverContext     *context,
                                        const GtkTextIter   *iter,
                                        GCancellable        *cancellable,
                                        GAsyncReadyCallback  callback,
                                        gpointer             user_data)
 {
-  IdeEditorHoverProvider *self = (IdeEditorHoverProvider *)provider;
+  GbpEditorHoverProvider *self = (GbpEditorHoverProvider *)provider;
   g_autoptr(IdeTask) task = NULL;
   GtkTextBuffer *buffer;
 
-  g_assert (IDE_IS_EDITOR_HOVER_PROVIDER (self));
+  g_assert (GBP_IS_EDITOR_HOVER_PROVIDER (self));
   g_assert (IDE_IS_HOVER_CONTEXT (context));
   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
 
   task = ide_task_new (self, cancellable, callback, user_data);
-  ide_task_set_source_tag (task, ide_editor_hover_provider_hover_async);
+  ide_task_set_source_tag (task, gbp_editor_hover_provider_hover_async);
 
   buffer = gtk_text_iter_get_buffer (iter);
 
   if (IDE_IS_BUFFER (buffer))
     {
+      GFile *file = ide_buffer_get_file (IDE_BUFFER (buffer));
+      guint line = gtk_text_iter_get_line (iter);
+      IdeDiagnostics *diagnostics;
       IdeDiagnostic *diag;
 
-      diag = ide_buffer_get_diagnostic_at_iter (IDE_BUFFER (buffer), iter);
-
-      if (diag != NULL)
+      if ((diagnostics = ide_buffer_get_diagnostics (IDE_BUFFER (buffer))) &&
+          (diag = ide_diagnostics_get_diagnostic_at_line (diagnostics, file, line)))
         {
           g_autoptr(IdeMarkedContent) content = NULL;
           g_autofree gchar *text = ide_diagnostic_get_text_for_display (diag);
@@ -85,7 +87,7 @@ ide_editor_hover_provider_hover_async (IdeHoverProvider    *provider,
 }
 
 static gboolean
-ide_editor_hover_provider_hover_finish (IdeHoverProvider  *self,
+gbp_editor_hover_provider_hover_finish (IdeHoverProvider  *self,
                                         GAsyncResult      *result,
                                         GError           **error)
 {
@@ -98,19 +100,19 @@ ide_editor_hover_provider_hover_finish (IdeHoverProvider  *self,
 static void
 hover_provider_iface_init (IdeHoverProviderInterface *iface)
 {
-  iface->hover_async = ide_editor_hover_provider_hover_async;
-  iface->hover_finish = ide_editor_hover_provider_hover_finish;
+  iface->hover_async = gbp_editor_hover_provider_hover_async;
+  iface->hover_finish = gbp_editor_hover_provider_hover_finish;
 }
 
-G_DEFINE_TYPE_WITH_CODE (IdeEditorHoverProvider, ide_editor_hover_provider, G_TYPE_OBJECT,
+G_DEFINE_TYPE_WITH_CODE (GbpEditorHoverProvider, gbp_editor_hover_provider, G_TYPE_OBJECT,
                          G_IMPLEMENT_INTERFACE (IDE_TYPE_HOVER_PROVIDER, hover_provider_iface_init))
 
 static void
-ide_editor_hover_provider_class_init (IdeEditorHoverProviderClass *klass)
+gbp_editor_hover_provider_class_init (GbpEditorHoverProviderClass *klass)
 {
 }
 
 static void
-ide_editor_hover_provider_init (IdeEditorHoverProvider *self)
+gbp_editor_hover_provider_init (GbpEditorHoverProvider *self)
 {
 }
diff --git a/src/libide/editor/ide-editor-hover-provider.h b/src/plugins/editor/gbp-editor-hover-provider.h
similarity index 77%
rename from src/libide/editor/ide-editor-hover-provider.h
rename to src/plugins/editor/gbp-editor-hover-provider.h
index 151be1eff..baacdde4d 100644
--- a/src/libide/editor/ide-editor-hover-provider.h
+++ b/src/plugins/editor/gbp-editor-hover-provider.h
@@ -1,4 +1,4 @@
-/* ide-editor-hover-provider.h
+/* gbp-editor-hover-provider.h
  *
  * Copyright 2018-2019 Christian Hergert <chergert redhat com>
  *
@@ -20,12 +20,12 @@
 
 #pragma once
 
-#include "hover/ide-hover-provider.h"
+#include "ide-hover-provider.h"
 
 G_BEGIN_DECLS
 
-#define IDE_TYPE_EDITOR_HOVER_PROVIDER (ide_editor_hover_provider_get_type())
+#define GBP_TYPE_EDITOR_HOVER_PROVIDER (gbp_editor_hover_provider_get_type())
 
-G_DECLARE_FINAL_TYPE (IdeEditorHoverProvider, ide_editor_hover_provider, IDE, EDITOR_HOVER_PROVIDER, GObject)
+G_DECLARE_FINAL_TYPE (GbpEditorHoverProvider, gbp_editor_hover_provider, GBP, EDITOR_HOVER_PROVIDER, GObject)
 
 G_END_DECLS
diff --git a/src/libide/editor/ide-editor-session-addin.c b/src/plugins/editor/gbp-editor-session-addin.c
similarity index 72%
rename from src/libide/editor/ide-editor-session-addin.c
rename to src/plugins/editor/gbp-editor-session-addin.c
index a7470df73..61b782242 100644
--- a/src/libide/editor/ide-editor-session-addin.c
+++ b/src/plugins/editor/gbp-editor-session-addin.c
@@ -1,4 +1,4 @@
-/* ide-editor-session-addin.c
+/* gbp-editor-session-addin.c
  *
  * Copyright 2018-2019 Christian Hergert <chergert redhat com>
  *
@@ -18,30 +18,21 @@
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
 
-#define G_LOG_DOMAIN "ide-editor-session-addin"
+#define G_LOG_DOMAIN "gbp-editor-session-addin"
 
 #include "config.h"
 
-#include <gtk/gtk.h>
-
-#include "ide-context.h"
-#include "ide-debug.h"
-
-#include "buffers/ide-buffer.h"
-#include "buffers/ide-buffer-manager.h"
-#include "editor/ide-editor-session-addin.h"
-#include "editor/ide-editor-perspective.h"
-#include "editor/ide-editor-private.h"
-#include "editor/ide-editor-view.h"
-#include "files/ide-file.h"
-#include "layout/ide-layout-grid.h"
-#include "layout/ide-layout-grid-column.h"
-#include "layout/ide-layout-private.h"
-#include "layout/ide-layout-stack.h"
-#include "threading/ide-task.h"
-#include "workbench/ide-workbench.h"
-
-struct _IdeEditorSessionAddin
+#include <libide-code.h>
+#include <libide-gui.h>
+#include <libide-editor.h>
+#include <libide-threading.h>
+
+#include "ide-editor-private.h"
+#include "ide-gui-private.h"
+
+#include "gbp-editor-session-addin.h"
+
+struct _GbpEditorSessionAddin
 {
   IdeObject parent_instance;
 };
@@ -62,17 +53,30 @@ typedef struct
 
 typedef struct
 {
-  GArray *items;
-  gint    active;
+  IdeWorkspace *workspace;
+  GArray       *items;
+  gint          active;
 } LoadState;
 
 static void
 load_state_free (LoadState *state)
 {
   g_clear_pointer (&state->items, g_array_unref);
+  g_clear_object (&state->workspace);
   g_slice_free (LoadState, state);
 }
 
+static IdeWorkspace *
+find_workspace (IdeWorkbench *workbench)
+{
+  IdeWorkspace *workspace;
+
+  if (!(workspace = ide_workbench_get_workspace_by_type (workbench, IDE_TYPE_PRIMARY_WORKSPACE)))
+    workspace = ide_workbench_get_workspace_by_type (workbench, IDE_TYPE_EDITOR_WORKSPACE);
+
+  return workspace;
+}
+
 static gint
 compare_item (gconstpointer a,
               gconstpointer b)
@@ -98,10 +102,10 @@ clear_item (Item *item)
 }
 
 static void
-get_view_position (IdeLayoutView *view,
-                   gint          *out_column,
-                   gint          *out_row,
-                   gint          *out_depth)
+get_view_position (IdePage *view,
+                   gint    *out_column,
+                   gint    *out_row,
+                   gint    *out_depth)
 {
   GtkWidget *column;
   GtkWidget *grid;
@@ -110,7 +114,7 @@ get_view_position (IdeLayoutView *view,
   gint depth;
   gint index_;
 
-  g_assert (IDE_IS_LAYOUT_VIEW (view));
+  g_assert (IDE_IS_PAGE (view));
   g_assert (out_column != NULL);
   g_assert (out_row != NULL);
 
@@ -119,9 +123,9 @@ get_view_position (IdeLayoutView *view,
   *out_depth = 0;
 
   stack = gtk_widget_get_ancestor (GTK_WIDGET (view), GTK_TYPE_STACK);
-  lstack = gtk_widget_get_ancestor (GTK_WIDGET (stack), IDE_TYPE_LAYOUT_STACK);
-  column = gtk_widget_get_ancestor (GTK_WIDGET (stack), IDE_TYPE_LAYOUT_GRID_COLUMN);
-  grid = gtk_widget_get_ancestor (GTK_WIDGET (column), IDE_TYPE_LAYOUT_GRID);
+  lstack = gtk_widget_get_ancestor (GTK_WIDGET (stack), IDE_TYPE_FRAME);
+  column = gtk_widget_get_ancestor (GTK_WIDGET (stack), IDE_TYPE_GRID_COLUMN);
+  grid = gtk_widget_get_ancestor (GTK_WIDGET (column), IDE_TYPE_GRID);
 
   gtk_container_child_get (GTK_CONTAINER (stack), GTK_WIDGET (view),
                            "position", &depth,
@@ -140,27 +144,25 @@ get_view_position (IdeLayoutView *view,
 }
 
 static void
-ide_editor_session_addin_foreach_view_cb (GtkWidget *widget,
+gbp_editor_session_addin_foreach_page_cb (GtkWidget *widget,
                                           gpointer   user_data)
 {
-  IdeLayoutView *view = (IdeLayoutView *)widget;
+  IdePage *view = (IdePage *)widget;
   GArray *items = user_data;
 
-  g_assert (IDE_IS_LAYOUT_VIEW (view));
+  g_assert (IDE_IS_PAGE (view));
   g_assert (items != NULL);
 
-  if (IDE_IS_EDITOR_VIEW (view))
+  if (IDE_IS_EDITOR_PAGE (view))
     {
-      IdeBuffer *buffer = ide_editor_view_get_buffer (IDE_EDITOR_VIEW (view));
-      IdeFile *file = ide_buffer_get_file (buffer);
-      IdeEditorSearch *search = ide_editor_view_get_search (IDE_EDITOR_VIEW (view));
+      IdeBuffer *buffer = ide_editor_page_get_buffer (IDE_EDITOR_PAGE (view));
+      GFile *file = ide_buffer_get_file (buffer);
+      IdeEditorSearch *search = ide_editor_page_get_search (IDE_EDITOR_PAGE (view));
       Item item = { 0 };
 
-      if (!ide_file_get_is_temporary (file))
+      if (!ide_buffer_get_is_temporary (buffer))
         {
-          GFile *gfile = ide_file_get_file (file);
-
-          item.uri = g_file_get_uri (gfile);
+          item.uri = g_file_get_uri (file);
           get_view_position (view, &item.column, &item.row, &item.depth);
 
           item.search.keyword = g_strdup (ide_editor_search_get_search_text (search));
@@ -176,35 +178,32 @@ ide_editor_session_addin_foreach_view_cb (GtkWidget *widget,
 }
 
 static void
-ide_editor_session_addin_save_async (IdeSessionAddin     *addin,
+gbp_editor_session_addin_save_async (IdeSessionAddin     *addin,
+                                     IdeWorkbench        *workbench,
                                      GCancellable        *cancellable,
                                      GAsyncReadyCallback  callback,
                                      gpointer             user_data)
 {
-  IdeEditorSessionAddin *self = (IdeEditorSessionAddin *)addin;
+  GbpEditorSessionAddin *self = (GbpEditorSessionAddin *)addin;
   g_autoptr(IdeTask) task = NULL;
   g_autoptr(GArray) items = NULL;
   GVariantBuilder builder;
-  IdeContext *context;
-  GtkWidget *workbench;
 
   IDE_ENTRY;
 
   g_assert (IDE_IS_SESSION_ADDIN (self));
+  g_assert (IDE_IS_WORKBENCH (workbench));
   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
 
   task = ide_task_new (addin, cancellable, callback, user_data);
-  ide_task_set_source_tag (task, ide_editor_session_addin_save_async);
-
-  context = ide_object_get_context (IDE_OBJECT (self));
-  workbench = ide_context_get_workbench (context);
+  ide_task_set_source_tag (task, gbp_editor_session_addin_save_async);
 
   items = g_array_new (FALSE, FALSE, sizeof (Item));
   g_array_set_clear_func (items, (GDestroyNotify)clear_item);
 
-  ide_workbench_views_foreach (IDE_WORKBENCH (workbench),
-                               ide_editor_session_addin_foreach_view_cb,
-                               items);
+  ide_workbench_foreach_page (workbench,
+                              gbp_editor_session_addin_foreach_page_cb,
+                              items);
 
   g_array_sort (items, compare_item);
 
@@ -238,34 +237,33 @@ ide_editor_session_addin_save_async (IdeSessionAddin     *addin,
 }
 
 static GVariant *
-ide_editor_session_addin_save_finish (IdeSessionAddin  *self,
+gbp_editor_session_addin_save_finish (IdeSessionAddin  *self,
                                       GAsyncResult     *result,
                                       GError          **error)
 {
-  g_assert (IDE_IS_EDITOR_SESSION_ADDIN (self));
+  g_assert (GBP_IS_EDITOR_SESSION_ADDIN (self));
   g_assert (IDE_IS_TASK (result));
 
   return ide_task_propagate_pointer (IDE_TASK (result), error);
 }
 
 static void
-load_state_finish (IdeEditorSessionAddin *self,
+load_state_finish (GbpEditorSessionAddin *self,
                    LoadState             *state)
 {
   IdeBufferManager *bufmgr;
-  IdePerspective *editor;
-  IdeLayoutGrid *grid;
   IdeContext *context;
-  GtkWidget *workbench;
+  IdeSurface *editor;
+  IdeGrid *grid;
 
-  g_assert (IDE_IS_EDITOR_SESSION_ADDIN (self));
+  g_assert (GBP_IS_EDITOR_SESSION_ADDIN (self));
   g_assert (state != NULL);
 
   context = ide_object_get_context (IDE_OBJECT (self));
-  bufmgr = ide_context_get_buffer_manager (context);
-  workbench = ide_context_get_workbench (context);
-  editor = ide_workbench_get_perspective_by_name (IDE_WORKBENCH (workbench), "editor");
-  grid = ide_editor_perspective_get_grid (IDE_EDITOR_PERSPECTIVE (editor));
+  bufmgr = ide_buffer_manager_from_context (context);
+
+  editor = ide_workspace_get_surface_by_name (state->workspace, "editor");
+  grid = ide_editor_surface_get_grid (IDE_EDITOR_SURFACE (editor));
 
   /* Now restore views in the proper place */
 
@@ -273,10 +271,10 @@ load_state_finish (IdeEditorSessionAddin *self,
     {
       const Item *item = &g_array_index (state->items, Item, i);
       g_autoptr(GFile) file = NULL;
-      IdeLayoutGridColumn *column;
+      IdeGridColumn *column;
       IdeEditorSearch *search;
-      IdeLayoutStack *stack;
-      IdeEditorView *view;
+      IdeFrame *stack;
+      IdeEditorPage *view;
       IdeBuffer *buffer;
 
       file = g_file_new_for_uri (item->uri);
@@ -287,15 +285,15 @@ load_state_finish (IdeEditorSessionAddin *self,
           continue;
         }
 
-      column = ide_layout_grid_get_nth_column (grid, item->column);
-      stack = _ide_layout_grid_get_nth_stack_for_column (grid, column, item->row);
+      column = ide_grid_get_nth_column (grid, item->column);
+      stack = _ide_grid_get_nth_stack_for_column (grid, column, item->row);
 
-      view = g_object_new (IDE_TYPE_EDITOR_VIEW,
+      view = g_object_new (IDE_TYPE_EDITOR_PAGE,
                            "buffer", buffer,
                            "visible", TRUE,
                            NULL);
 
-      search = ide_editor_view_get_search (view);
+      search = ide_editor_page_get_search (view);
 
       ide_editor_search_set_search_text (search, item->search.keyword);
       ide_editor_search_set_at_word_boundaries (search, item->search.at_word_boundaries);
@@ -307,7 +305,7 @@ load_state_finish (IdeEditorSessionAddin *self,
 }
 
 static void
-ide_editor_session_addin_load_file_cb (GObject      *object,
+gbp_editor_session_addin_load_file_cb (GObject      *object,
                                        GAsyncResult *result,
                                        gpointer      user_data)
 {
@@ -315,7 +313,7 @@ ide_editor_session_addin_load_file_cb (GObject      *object,
   g_autoptr(IdeBuffer) loaded = NULL;
   g_autoptr(IdeTask) task = user_data;
   g_autoptr(GError) error = NULL;
-  IdeEditorSessionAddin *self;
+  GbpEditorSessionAddin *self;
   LoadState *state;
 
   g_assert (IDE_IS_BUFFER_MANAGER (bufmgr));
@@ -331,7 +329,7 @@ ide_editor_session_addin_load_file_cb (GObject      *object,
   g_assert (state != NULL);
   g_assert (state->items != NULL);
   g_assert (state->active > 0);
-  g_assert (IDE_IS_EDITOR_SESSION_ADDIN (self));
+  g_assert (GBP_IS_EDITOR_SESSION_ADDIN (self));
 
   state->active--;
 
@@ -351,7 +349,7 @@ restore_file (GObject      *source,
   g_autoptr(IdeTask) task = user_data;
   g_autoptr(GError) error = NULL;
   g_autoptr(GFileInfo) info = NULL;
-  IdeEditorSessionAddin *self;
+  GbpEditorSessionAddin *self;
   LoadState *load_state;
 
   IDE_ENTRY;
@@ -363,22 +361,20 @@ restore_file (GObject      *source,
   self = ide_task_get_source_object (task);
   load_state = ide_task_get_task_data (task);
 
-  g_assert (IDE_IS_EDITOR_SESSION_ADDIN (self));
+  g_assert (GBP_IS_EDITOR_SESSION_ADDIN (self));
   g_assert (load_state != NULL);
 
   if ((info = g_file_query_info_finish (file, result, &error)))
     {
       IdeContext *context = ide_object_get_context (IDE_OBJECT (self));
-      IdeBufferManager *bufmgr = ide_context_get_buffer_manager (context);
-      g_autoptr(IdeFile) ifile = ide_file_new (context, file);
+      IdeBufferManager *bufmgr = ide_buffer_manager_from_context (context);
 
       ide_buffer_manager_load_file_async (bufmgr,
-                                          ifile,
-                                          FALSE,
-                                          IDE_WORKBENCH_OPEN_FLAGS_NO_VIEW,
-                                          NULL,
+                                          file,
+                                          IDE_BUFFER_OPEN_FLAGS_NO_VIEW,
                                           ide_task_get_cancellable (task),
-                                          ide_editor_session_addin_load_file_cb,
+                                          NULL,
+                                          gbp_editor_session_addin_load_file_cb,
                                           g_object_ref (task));
     }
   else
@@ -394,19 +390,20 @@ restore_file (GObject      *source,
 }
 
 static void
-load_task_completed_cb (IdeTask              *task,
-                        GParamSpec           *pspec,
-                        IdeEditorPerspective *perspective)
+load_task_completed_cb (IdeTask          *task,
+                        GParamSpec       *pspec,
+                        IdeEditorSurface *surface)
 {
   g_assert (IDE_IS_TASK (task));
-  g_assert (IDE_IS_EDITOR_PERSPECTIVE (perspective));
+  g_assert (IDE_IS_EDITOR_SURFACE (surface));
 
   /* Always show the grid after the task completes */
-  _ide_editor_perspective_set_loading (perspective, FALSE);
+  _ide_editor_surface_set_loading (surface, FALSE);
 }
 
 static void
-ide_editor_session_addin_restore_async (IdeSessionAddin     *addin,
+gbp_editor_session_addin_restore_async (IdeSessionAddin     *addin,
+                                        IdeWorkbench        *workbench,
                                         GVariant            *state,
                                         GCancellable        *cancellable,
                                         GAsyncReadyCallback  callback,
@@ -417,23 +414,35 @@ ide_editor_session_addin_restore_async (IdeSessionAddin     *addin,
   g_autoptr(GSettings) settings = NULL;
   const gchar *uri;
   LoadState *load_state;
-  IdeContext *context;
-  IdeWorkbench *workbench;
-  IdePerspective *editor;
+  IdeWorkspace *workspace;
+  IdeSurface *editor;
   GVariantIter iter;
   GVariant *extra = NULL;
   const gchar *format = "(&siii)";
   gint column, row, depth;
 
-  g_assert (IDE_IS_EDITOR_SESSION_ADDIN (addin));
+  g_assert (GBP_IS_EDITOR_SESSION_ADDIN (addin));
   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
 
-  context = ide_object_get_context (IDE_OBJECT (addin));
-  workbench = IDE_WORKBENCH (ide_context_get_workbench (context));
-  editor = ide_workbench_get_perspective_by_name (workbench, "editor");
-
   task = ide_task_new (addin, cancellable, callback, user_data);
-  ide_task_set_source_tag (task, ide_editor_session_addin_restore_async);
+  ide_task_set_source_tag (task, gbp_editor_session_addin_restore_async);
+
+  if (state == NULL)
+    {
+      ide_task_return_boolean (task, TRUE);
+      return;
+    }
+
+  if (!(workspace = find_workspace (workbench)) ||
+      !(editor = ide_workspace_get_surface_by_name (workspace, "editor")))
+    {
+      ide_task_return_new_error (task,
+                                 G_IO_ERROR,
+                                 G_IO_ERROR_FAILED,
+                                 "NO editor surface to restore documents");
+      return;
+    }
+
   g_signal_connect_object (task,
                            "notify::completed",
                            G_CALLBACK (load_task_completed_cb),
@@ -452,6 +461,7 @@ ide_editor_session_addin_restore_async (IdeSessionAddin     *addin,
 
   load_state = g_slice_new0 (LoadState);
   load_state->items = g_array_new (FALSE, FALSE, sizeof (Item));
+  load_state->workspace = g_object_ref (workspace);
   g_array_set_clear_func (load_state->items, (GDestroyNotify)clear_item);
   ide_task_set_task_data (task, load_state, load_state_free);
 
@@ -517,15 +527,15 @@ ide_editor_session_addin_restore_async (IdeSessionAddin     *addin,
     }
 
   /* Hide the grid until we've loaded */
-  _ide_editor_perspective_set_loading (IDE_EDITOR_PERSPECTIVE (editor), TRUE);
+  _ide_editor_surface_set_loading (IDE_EDITOR_SURFACE (editor), TRUE);
 }
 
 static gboolean
-ide_editor_session_addin_restore_finish (IdeSessionAddin  *self,
+gbp_editor_session_addin_restore_finish (IdeSessionAddin  *self,
                                          GAsyncResult     *result,
                                          GError          **error)
 {
-  g_assert (IDE_IS_EDITOR_SESSION_ADDIN (self));
+  g_assert (GBP_IS_EDITOR_SESSION_ADDIN (self));
   g_assert (IDE_IS_TASK (result));
 
   return ide_task_propagate_boolean (IDE_TASK (result), error);
@@ -534,21 +544,21 @@ ide_editor_session_addin_restore_finish (IdeSessionAddin  *self,
 static void
 session_addin_iface_init (IdeSessionAddinInterface *iface)
 {
-  iface->save_async = ide_editor_session_addin_save_async;
-  iface->save_finish = ide_editor_session_addin_save_finish;
-  iface->restore_async = ide_editor_session_addin_restore_async;
-  iface->restore_finish = ide_editor_session_addin_restore_finish;
+  iface->save_async = gbp_editor_session_addin_save_async;
+  iface->save_finish = gbp_editor_session_addin_save_finish;
+  iface->restore_async = gbp_editor_session_addin_restore_async;
+  iface->restore_finish = gbp_editor_session_addin_restore_finish;
 }
 
-G_DEFINE_TYPE_WITH_CODE (IdeEditorSessionAddin, ide_editor_session_addin, IDE_TYPE_OBJECT,
+G_DEFINE_TYPE_WITH_CODE (GbpEditorSessionAddin, gbp_editor_session_addin, IDE_TYPE_OBJECT,
                          G_IMPLEMENT_INTERFACE (IDE_TYPE_SESSION_ADDIN, session_addin_iface_init))
 
 static void
-ide_editor_session_addin_class_init (IdeEditorSessionAddinClass *klass)
+gbp_editor_session_addin_class_init (GbpEditorSessionAddinClass *klass)
 {
 }
 
 static void
-ide_editor_session_addin_init (IdeEditorSessionAddin *self)
+gbp_editor_session_addin_init (GbpEditorSessionAddin *self)
 {
 }
diff --git a/src/libide/editor/ide-editor-session-addin.h b/src/plugins/editor/gbp-editor-session-addin.h
similarity index 77%
rename from src/libide/editor/ide-editor-session-addin.h
rename to src/plugins/editor/gbp-editor-session-addin.h
index 637d8eeab..7fa6b938c 100644
--- a/src/libide/editor/ide-editor-session-addin.h
+++ b/src/plugins/editor/gbp-editor-session-addin.h
@@ -1,4 +1,4 @@
-/* ide-editor-session-addin.h
+/* gbp-editor-session-addin.h
  *
  * Copyright 2018-2019 Christian Hergert <chergert redhat com>
  *
@@ -20,12 +20,12 @@
 
 #pragma once
 
-#include "session/ide-session-addin.h"
+#include <libide-core.h>
 
 G_BEGIN_DECLS
 
-#define IDE_TYPE_EDITOR_SESSION_ADDIN (ide_editor_session_addin_get_type())
+#define GBP_TYPE_EDITOR_SESSION_ADDIN (gbp_editor_session_addin_get_type())
 
-G_DECLARE_FINAL_TYPE (IdeEditorSessionAddin, ide_editor_session_addin, IDE, EDITOR_SESSION_ADDIN, IdeObject)
+G_DECLARE_FINAL_TYPE (GbpEditorSessionAddin, gbp_editor_session_addin, GBP, EDITOR_SESSION_ADDIN, IdeObject)
 
 G_END_DECLS
diff --git a/src/plugins/editor/gbp-editor-workbench-addin.c b/src/plugins/editor/gbp-editor-workbench-addin.c
new file mode 100644
index 000000000..6d6079bd5
--- /dev/null
+++ b/src/plugins/editor/gbp-editor-workbench-addin.c
@@ -0,0 +1,341 @@
+/* gbp-editor-workbench-addin.c
+ *
+ * Copyright 2015-2019 Christian Hergert <christian hergert me>
+ *
+ * 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-editor-workbench-addin"
+
+#include "config.h"
+
+#include <dazzle.h>
+#include <glib/gi18n.h>
+#include <gtksourceview/gtksource.h>
+#include <libide-code.h>
+#include <libide-editor.h>
+#include <libide-gui.h>
+#include <libide-io.h>
+#include <libide-threading.h>
+#include <string.h>
+
+#include "gbp-editor-workbench-addin.h"
+
+struct _GbpEditorWorkbenchAddin
+{
+  GObject       parent_instance;
+  IdeWorkbench *workbench;
+};
+
+typedef struct
+{
+  GFile              *file;
+  IdeBufferOpenFlags  flags;
+  gint                at_line;
+  gint                at_line_offset;
+} OpenFileTaskData;
+
+static void ide_workbench_addin_iface_init (IdeWorkbenchAddinInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GbpEditorWorkbenchAddin, gbp_editor_workbench_addin, G_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (IDE_TYPE_WORKBENCH_ADDIN,
+                                               ide_workbench_addin_iface_init))
+
+static void
+open_file_task_data_free (gpointer data)
+{
+  OpenFileTaskData *td = data;
+
+  g_clear_object (&td->file);
+  g_slice_free (OpenFileTaskData, td);
+}
+
+static void
+gbp_editor_workbench_addin_class_init (GbpEditorWorkbenchAddinClass *klass)
+{
+}
+
+static void
+gbp_editor_workbench_addin_init (GbpEditorWorkbenchAddin *self)
+{
+}
+
+static void
+gbp_editor_workbench_addin_load (IdeWorkbenchAddin *addin,
+                                 IdeWorkbench      *workbench)
+{
+  GbpEditorWorkbenchAddin *self = (GbpEditorWorkbenchAddin *)addin;
+
+  g_assert (GBP_IS_EDITOR_WORKBENCH_ADDIN (self));
+  g_assert (IDE_IS_WORKBENCH (workbench));
+  g_assert (self->workbench == NULL);
+
+  self->workbench = workbench;
+}
+
+static void
+gbp_editor_workbench_addin_unload (IdeWorkbenchAddin *addin,
+                                   IdeWorkbench      *workbench)
+{
+  GbpEditorWorkbenchAddin *self = (GbpEditorWorkbenchAddin *)addin;
+
+  g_assert (GBP_IS_EDITOR_WORKBENCH_ADDIN (self));
+  g_assert (IDE_IS_WORKBENCH (workbench));
+
+  self->workbench = NULL;
+}
+
+static gboolean
+gbp_editor_workbench_addin_can_open (IdeWorkbenchAddin *addin,
+                                     GFile             *file,
+                                     const gchar       *content_type,
+                                     gint              *priority)
+{
+  const gchar *path;
+
+  g_assert (GBP_IS_EDITOR_WORKBENCH_ADDIN (addin));
+  g_assert (G_IS_FILE (file));
+  g_assert (priority != NULL);
+
+  *priority = 0;
+
+  path = g_file_peek_path (file);
+
+  if (path != NULL || content_type != NULL)
+    {
+      GtkSourceLanguageManager *manager;
+      GtkSourceLanguage *language;
+
+      manager = gtk_source_language_manager_get_default ();
+      language = gtk_source_language_manager_guess_language (manager, path, content_type);
+
+      if (language != NULL)
+        return TRUE;
+    }
+
+  if (content_type != NULL)
+    {
+      g_autofree gchar *text_type = NULL;
+
+      text_type = g_content_type_from_mime_type ("text/plain");
+      return g_content_type_is_a (content_type, text_type);
+    }
+
+  return FALSE;
+}
+
+static void
+find_workspace_surface_cb (GtkWidget *widget,
+                           gpointer   user_data)
+{
+  IdeSurface **surface = user_data;
+
+  g_assert (IDE_IS_WORKSPACE (widget));
+  g_assert (surface != NULL);
+  g_assert (*surface == NULL || IDE_IS_SURFACE (*surface));
+
+  if (*surface == NULL)
+    {
+      *surface = ide_workspace_get_surface_by_name (IDE_WORKSPACE (widget), "editor");
+      if (!IDE_IS_EDITOR_SURFACE (*surface))
+        *surface = NULL;
+    }
+}
+
+static void
+gbp_editor_workbench_addin_open_at_cb (GObject      *object,
+                                       GAsyncResult *result,
+                                       gpointer      user_data)
+{
+  IdeBufferManager *buffer_manager = (IdeBufferManager *)object;
+  GbpEditorWorkbenchAddin *self;
+  g_autoptr(IdeBuffer) buffer = NULL;
+  g_autoptr(IdeTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  OpenFileTaskData *state;
+  IdeEditorSurface *surface = NULL;
+
+  g_assert (IDE_IS_BUFFER_MANAGER (buffer_manager));
+  g_assert (IDE_IS_TASK (task));
+
+  self = ide_task_get_source_object (task);
+  g_assert (GBP_IS_EDITOR_WORKBENCH_ADDIN (self));
+
+  buffer = ide_buffer_manager_load_file_finish (buffer_manager, result, &error);
+
+  if (buffer == NULL)
+    {
+      IDE_TRACE_MSG ("%s", error->message);
+      ide_task_return_error (task, g_steal_pointer (&error));
+      return;
+    }
+
+  if (self->workbench == NULL)
+    goto failure;
+
+  ide_workbench_foreach_workspace (self->workbench,
+                                   find_workspace_surface_cb,
+                                   &surface);
+
+  if (!IDE_IS_EDITOR_SURFACE (surface))
+    goto failure;
+
+  state = ide_task_get_task_data (task);
+
+  if (state->at_line > -1)
+    {
+      g_autoptr(IdeLocation) location = NULL;
+
+      location = ide_location_new (state->file,
+                                   state->at_line,
+                                   state->at_line_offset);
+      ide_editor_surface_focus_location (surface, location);
+    }
+
+  if (surface != NULL &&
+      !(state->flags & IDE_BUFFER_OPEN_FLAGS_NO_VIEW) &&
+      !(state->flags & IDE_BUFFER_OPEN_FLAGS_BACKGROUND))
+    ide_editor_surface_focus_buffer_in_current_stack (surface, buffer);
+
+failure:
+  ide_task_return_boolean (task, TRUE);
+}
+
+static void
+gbp_editor_workbench_addin_open_at_async (IdeWorkbenchAddin   *addin,
+                                          GFile               *file,
+                                          const gchar         *content_type,
+                                          gint                 at_line,
+                                          gint                 at_line_offset,
+                                          IdeBufferOpenFlags   flags,
+                                          GCancellable        *cancellable,
+                                          GAsyncReadyCallback  callback,
+                                          gpointer             user_data)
+{
+  GbpEditorWorkbenchAddin *self = (GbpEditorWorkbenchAddin *)addin;
+  IdeBufferManager *buffer_manager;
+  IdeContext *context;
+  OpenFileTaskData *state;
+  g_autoptr(IdeTask) task = NULL;
+
+  g_assert (GBP_IS_EDITOR_WORKBENCH_ADDIN (self));
+  g_assert (G_IS_FILE (file));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+  g_assert (IDE_IS_WORKBENCH (self->workbench));
+
+  task = ide_task_new (self, cancellable, callback, user_data);
+  state = g_slice_new0 (OpenFileTaskData);
+  state->flags = flags;
+  state->file = g_object_ref (file);
+  state->at_line = at_line;
+  state->at_line_offset = at_line_offset;
+  ide_task_set_task_data (task, state, open_file_task_data_free);
+
+  context = ide_workbench_get_context (self->workbench);
+  buffer_manager = ide_buffer_manager_from_context (context);
+
+  ide_buffer_manager_load_file_async (buffer_manager,
+                                      file,
+                                      state->flags,
+                                      cancellable,
+                                      NULL,
+                                      gbp_editor_workbench_addin_open_at_cb,
+                                      g_steal_pointer (&task));
+}
+
+static void
+gbp_editor_workbench_addin_open_async (IdeWorkbenchAddin   *addin,
+                                       GFile               *file,
+                                       const gchar         *content_type,
+                                       IdeBufferOpenFlags   flags,
+                                       GCancellable        *cancellable,
+                                       GAsyncReadyCallback  callback,
+                                       gpointer             user_data)
+{
+  gbp_editor_workbench_addin_open_at_async (addin, file, content_type, -1, -1, flags, cancellable, callback, 
user_data);
+}
+
+static gboolean
+gbp_editor_workbench_addin_open_finish (IdeWorkbenchAddin  *addin,
+                                        GAsyncResult       *result,
+                                        GError            **error)
+{
+  g_assert (GBP_IS_EDITOR_WORKBENCH_ADDIN (addin));
+  g_assert (IDE_IS_TASK (result));
+
+  return ide_task_propagate_boolean (IDE_TASK (result), error);
+}
+
+static void
+new_editor_workspace_cb (GSimpleAction *action,
+                         GVariant      *param,
+                         gpointer       user_data)
+{
+  GbpEditorWorkbenchAddin *self = user_data;
+  IdeWorkspace *workspace;
+
+  g_assert (G_IS_SIMPLE_ACTION (action));
+  g_assert (GBP_IS_EDITOR_WORKBENCH_ADDIN (self));
+
+  workspace = g_object_new (IDE_TYPE_EDITOR_WORKSPACE,
+                            "application", IDE_APPLICATION_DEFAULT,
+                            NULL);
+  ide_workbench_add_workspace (self->workbench, workspace);
+  gtk_window_present (GTK_WINDOW (workspace));
+}
+
+static GActionEntry actions[] = {
+  { "new-editor-workspace", new_editor_workspace_cb },
+};
+
+static void
+gbp_editor_workbench_addin_workspace_added (IdeWorkbenchAddin *addin,
+                                            IdeWorkspace      *workspace)
+{
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_WORKBENCH_ADDIN (addin));
+  g_assert (IDE_IS_WORKSPACE (workspace));
+
+  g_action_map_add_action_entries (G_ACTION_MAP (workspace),
+                                   actions,
+                                   G_N_ELEMENTS (actions),
+                                   addin);
+}
+
+static void
+gbp_editor_workbench_addin_workspace_removed (IdeWorkbenchAddin *addin,
+                                              IdeWorkspace      *workspace)
+{
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_WORKBENCH_ADDIN (addin));
+  g_assert (IDE_IS_WORKSPACE (workspace));
+
+  for (guint i = 0; i < G_N_ELEMENTS (actions); i++)
+    g_action_map_remove_action (G_ACTION_MAP (workspace), actions[i].name);
+}
+
+static void
+ide_workbench_addin_iface_init (IdeWorkbenchAddinInterface *iface)
+{
+  iface->can_open = gbp_editor_workbench_addin_can_open;
+  iface->load = gbp_editor_workbench_addin_load;
+  iface->open_at_async = gbp_editor_workbench_addin_open_at_async;
+  iface->open_async = gbp_editor_workbench_addin_open_async;
+  iface->open_finish = gbp_editor_workbench_addin_open_finish;
+  iface->unload = gbp_editor_workbench_addin_unload;
+  iface->workspace_added = gbp_editor_workbench_addin_workspace_added;
+  iface->workspace_removed = gbp_editor_workbench_addin_workspace_removed;
+}
diff --git a/src/libide/editor/ide-editor-workbench-addin.h b/src/plugins/editor/gbp-editor-workbench-addin.h
similarity index 76%
rename from src/libide/editor/ide-editor-workbench-addin.h
rename to src/plugins/editor/gbp-editor-workbench-addin.h
index 478cbd4ff..9f844ac95 100644
--- a/src/libide/editor/ide-editor-workbench-addin.h
+++ b/src/plugins/editor/gbp-editor-workbench-addin.h
@@ -1,4 +1,4 @@
-/* ide-editor-workbench-addin.h
+/* gbp-editor-workbench-addin.h
  *
  * Copyright 2015-2019 Christian Hergert <christian hergert me>
  *
@@ -20,12 +20,12 @@
 
 #pragma once
 
-#include "workbench/ide-workbench-addin.h"
+#include <libide-gui.h>
 
 G_BEGIN_DECLS
 
-#define IDE_TYPE_EDITOR_WORKBENCH_ADDIN (ide_editor_workbench_addin_get_type())
+#define GBP_TYPE_EDITOR_WORKBENCH_ADDIN (gbp_editor_workbench_addin_get_type())
 
-G_DECLARE_FINAL_TYPE (IdeEditorWorkbenchAddin, ide_editor_workbench_addin, IDE, EDITOR_WORKBENCH_ADDIN, 
GObject)
+G_DECLARE_FINAL_TYPE (GbpEditorWorkbenchAddin, gbp_editor_workbench_addin, GBP, EDITOR_WORKBENCH_ADDIN, 
GObject)
 
 G_END_DECLS
diff --git a/src/plugins/editor/gbp-editor-workspace-addin.c b/src/plugins/editor/gbp-editor-workspace-addin.c
new file mode 100644
index 000000000..bbeaabdbf
--- /dev/null
+++ b/src/plugins/editor/gbp-editor-workspace-addin.c
@@ -0,0 +1,317 @@
+/* gbp-editor-workspace-addin.c
+ *
+ * Copyright 2018-2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-editor-workspace-addin"
+
+#include "config.h"
+
+#include <libide-editor.h>
+#include <libide-gui.h>
+
+#include "gbp-editor-workspace-addin.h"
+
+struct _GbpEditorWorkspaceAddin
+{
+  GObject               parent_instance;
+
+  DzlSignalGroup       *buffer_manager_signals;
+  DzlShortcutTooltip   *tooltip1;
+  DzlShortcutTooltip   *tooltip2;
+
+  IdeWorkspace         *workspace;
+  IdeEditorSurface     *surface;
+  GtkBox               *panels_box;
+  DzlMenuButton        *new_button;
+};
+
+static void
+find_topmost_editor (GtkWidget *widget,
+                     gpointer   user_data)
+{
+  IdeWorkspace **workspace = user_data;
+  IdeSurface *surface;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_WORKSPACE (widget));
+  g_assert (workspace != NULL);
+
+  if (*workspace)
+    return;
+
+  if ((surface = ide_workspace_get_surface_by_name (IDE_WORKSPACE (widget), "editor")) &&
+      IDE_IS_EDITOR_SURFACE (surface))
+    *workspace = IDE_WORKSPACE (widget);
+}
+
+static gboolean
+is_topmost_workspace_with_editor (GbpEditorWorkspaceAddin *self)
+{
+  IdeWorkbench *workbench;
+  IdeWorkspace *topmost = NULL;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_EDITOR_WORKSPACE_ADDIN (self));
+
+  workbench = ide_widget_get_workbench (GTK_WIDGET (self->workspace));
+  ide_workbench_foreach_workspace (workbench, find_topmost_editor, &topmost);
+
+  return topmost == self->workspace;
+}
+
+static void
+on_load_buffer (GbpEditorWorkspaceAddin *self,
+                IdeBuffer               *buffer,
+                gboolean                 create_new_view,
+                IdeBufferManager        *buffer_manager)
+{
+  g_autofree gchar *title = NULL;
+
+  g_assert (GBP_IS_EDITOR_WORKSPACE_ADDIN (self));
+  g_assert (IDE_IS_BUFFER (buffer));
+  g_assert (IDE_IS_BUFFER_MANAGER (buffer_manager));
+
+  /* We only want to create a new view when the buffer is originally created,
+   * not when it's reloaded.
+   */
+  if (!create_new_view)
+    return;
+
+  /* If another workspace is active and it has an editor surface, then we
+   * don't want to open the buffer in this window.
+   */
+  if (!is_topmost_workspace_with_editor (self))
+    return;
+
+  title = ide_buffer_dup_title (buffer);
+  g_debug ("Loading editor page for \"%s\"", title);
+
+  ide_editor_surface_focus_buffer (self->surface, buffer);
+}
+
+static void
+bind_buffer_manager (GbpEditorWorkspaceAddin *self,
+                     IdeBufferManager        *buffer_manager,
+                     DzlSignalGroup          *signal_group)
+{
+  guint n_items;
+
+  g_assert (GBP_IS_EDITOR_WORKSPACE_ADDIN (self));
+  g_assert (IDE_IS_BUFFER_MANAGER (buffer_manager));
+  g_assert (DZL_IS_SIGNAL_GROUP (signal_group));
+
+  n_items = g_list_model_get_n_items (G_LIST_MODEL (buffer_manager));
+
+  for (guint i = 0; i < n_items; i++)
+    {
+      g_autoptr(IdeBuffer) buffer = NULL;
+
+      buffer = g_list_model_get_item (G_LIST_MODEL (buffer_manager), i);
+      ide_editor_surface_focus_buffer (self->surface, buffer);
+    }
+}
+
+static void
+add_buttons (GbpEditorWorkspaceAddin *self,
+             IdeHeaderBar            *header)
+{
+  GtkWidget *button;
+
+  g_assert (GBP_IS_EDITOR_WORKSPACE_ADDIN (self));
+  g_assert (IDE_IS_HEADER_BAR (header));
+
+  self->new_button = g_object_new (DZL_TYPE_MENU_BUTTON,
+                                   "icon-name", "document-open-symbolic",
+                                   "focus-on-click", FALSE,
+                                   "show-arrow", TRUE,
+                                   "show-icons", FALSE,
+                                   "show-accels", FALSE,
+                                   "menu-id", "new-document-menu",
+                                   "visible", TRUE,
+                                   NULL);
+  g_signal_connect (self->new_button,
+                    "destroy",
+                    G_CALLBACK (gtk_widget_destroyed),
+                    &self->new_button);
+  ide_header_bar_add_primary (header, GTK_WIDGET (self->new_button));
+
+  self->panels_box = g_object_new (GTK_TYPE_BOX,
+                                   "margin-start", 6,
+                                   "margin-end", 6,
+                                   "visible", TRUE,
+                                   NULL);
+  g_signal_connect (self->panels_box,
+                    "destroy",
+                    G_CALLBACK (gtk_widget_destroyed),
+                    &self->panels_box);
+  dzl_gtk_widget_add_style_class (GTK_WIDGET (self->panels_box), "linked");
+  ide_header_bar_add_primary (header, GTK_WIDGET (self->panels_box));
+
+  button = g_object_new (GTK_TYPE_TOGGLE_BUTTON,
+                         "action-name", "dockbin.left-visible",
+                         "focus-on-click", FALSE,
+                         "child", g_object_new (GTK_TYPE_IMAGE,
+                                                "icon-name", "builder-view-left-pane-symbolic",
+                                                "margin-start", 12,
+                                                "margin-end", 12,
+                                                "visible", TRUE,
+                                                NULL),
+                         "visible", TRUE,
+                         NULL);
+  self->tooltip1 = g_object_new (DZL_TYPE_SHORTCUT_TOOLTIP,
+                                 "command-id", "org.gnome.builder.editor.navigation-panel",
+                                 "widget", button,
+                                 NULL);
+  gtk_container_add (GTK_CONTAINER (self->panels_box), button);
+
+  button = g_object_new (GTK_TYPE_TOGGLE_BUTTON,
+                         "action-name", "dockbin.bottom-visible",
+                         "focus-on-click", FALSE,
+                         "child", g_object_new (GTK_TYPE_IMAGE,
+                                                "icon-name", "builder-view-bottom-pane-symbolic",
+                                                "margin-start", 12,
+                                                "margin-end", 12,
+                                                "visible", TRUE,
+                                                NULL),
+                         "visible", TRUE,
+                         NULL);
+  self->tooltip2 = g_object_new (DZL_TYPE_SHORTCUT_TOOLTIP,
+                                 "command-id", "org.gnome.builder.editor.utilities-panel",
+                                 "widget", button,
+                                 NULL);
+  gtk_container_add (GTK_CONTAINER (self->panels_box), button);
+}
+
+static void
+gbp_editor_workspace_addin_load (IdeWorkspaceAddin *addin,
+                                 IdeWorkspace      *workspace)
+{
+  GbpEditorWorkspaceAddin *self = (GbpEditorWorkspaceAddin *)addin;
+  IdeBufferManager *buffer_manager;
+  IdeHeaderBar *header_bar;
+  IdeContext *context;
+
+  g_assert (GBP_IS_EDITOR_WORKSPACE_ADDIN (self));
+  g_assert (IDE_IS_WORKSPACE (workspace));
+  g_assert (IDE_IS_PRIMARY_WORKSPACE (workspace) ||
+            IDE_IS_EDITOR_WORKSPACE (workspace));
+
+  self->workspace = workspace;
+
+  /* Get our buffer manager for future use */
+  context = ide_widget_get_context (GTK_WIDGET (workspace));
+  buffer_manager = ide_buffer_manager_from_context (context);
+
+  /* Monitor buffer manager for new buffers */
+  self->buffer_manager_signals = dzl_signal_group_new (IDE_TYPE_BUFFER_MANAGER);
+  g_signal_connect_swapped (self->buffer_manager_signals,
+                            "bind",
+                            G_CALLBACK (bind_buffer_manager),
+                            self);
+  dzl_signal_group_connect_swapped (self->buffer_manager_signals,
+                                    "load-buffer",
+                                    G_CALLBACK (on_load_buffer),
+                                    self);
+  dzl_signal_group_set_target (self->buffer_manager_signals, buffer_manager);
+
+  /* Add buttons to the header bar */
+  header_bar = ide_workspace_get_header_bar (workspace);
+  add_buttons (self, header_bar);
+
+  /* Add the editor surface to the workspace */
+  self->surface = g_object_new (IDE_TYPE_EDITOR_SURFACE,
+                                "name", "editor",
+                                "visible", TRUE,
+                                NULL);
+  g_signal_connect (self->surface,
+                    "destroy",
+                    G_CALLBACK (gtk_widget_destroyed),
+                    &self->surface);
+  ide_workspace_add_surface (IDE_WORKSPACE (workspace), IDE_SURFACE (self->surface));
+  ide_workspace_set_visible_surface_name (IDE_WORKSPACE (workspace), "editor");
+}
+
+static void
+gbp_editor_workspace_addin_unload (IdeWorkspaceAddin *addin,
+                                   IdeWorkspace      *workspace)
+{
+  GbpEditorWorkspaceAddin *self = (GbpEditorWorkspaceAddin *)addin;
+
+  g_assert (GBP_IS_EDITOR_WORKSPACE_ADDIN (self));
+  g_assert (IDE_IS_WORKSPACE (workspace));
+  g_assert (IDE_IS_PRIMARY_WORKSPACE (workspace) ||
+            IDE_IS_EDITOR_WORKSPACE (workspace));
+
+  dzl_signal_group_set_target (self->buffer_manager_signals, NULL);
+  g_clear_object (&self->buffer_manager_signals);
+
+  if (self->surface != NULL)
+    gtk_widget_destroy (GTK_WIDGET (self->surface));
+
+  if (self->panels_box)
+    gtk_widget_destroy (GTK_WIDGET (self->panels_box));
+
+  if (self->new_button)
+    gtk_widget_destroy (GTK_WIDGET (self->new_button));
+
+  g_clear_object (&self->tooltip1);
+  g_clear_object (&self->tooltip2);
+
+  self->workspace = NULL;
+}
+
+static void
+gbp_editor_workspace_addin_surface_set (IdeWorkspaceAddin *addin,
+                                        IdeSurface        *surface)
+{
+  GbpEditorWorkspaceAddin *self = (GbpEditorWorkspaceAddin *)addin;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_EDITOR_WORKSPACE_ADDIN (self));
+  g_assert (!surface || IDE_IS_SURFACE (surface));
+
+  if (self->panels_box)
+    gtk_widget_set_visible (GTK_WIDGET (self->panels_box),
+                            IDE_IS_EDITOR_SURFACE (surface));
+  if (self->new_button)
+    gtk_widget_set_visible (GTK_WIDGET (self->new_button),
+                            IDE_IS_EDITOR_SURFACE (surface));
+}
+
+static void
+workspace_addin_iface_init (IdeWorkspaceAddinInterface *iface)
+{
+  iface->load = gbp_editor_workspace_addin_load;
+  iface->unload = gbp_editor_workspace_addin_unload;
+  iface->surface_set = gbp_editor_workspace_addin_surface_set;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GbpEditorWorkspaceAddin, gbp_editor_workspace_addin, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (IDE_TYPE_WORKSPACE_ADDIN,
+                                                workspace_addin_iface_init))
+
+static void
+gbp_editor_workspace_addin_class_init (GbpEditorWorkspaceAddinClass *klass)
+{
+}
+
+static void
+gbp_editor_workspace_addin_init (GbpEditorWorkspaceAddin *self)
+{
+}
diff --git a/src/plugins/editor/gbp-editor-workspace-addin.h b/src/plugins/editor/gbp-editor-workspace-addin.h
new file mode 100644
index 000000000..f7d204739
--- /dev/null
+++ b/src/plugins/editor/gbp-editor-workspace-addin.h
@@ -0,0 +1,31 @@
+/* gbp-editor-workspace-addin.h
+ *
+ * Copyright 2018-2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libide-editor.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_EDITOR_WORKSPACE_ADDIN (gbp_editor_workspace_addin_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpEditorWorkspaceAddin, gbp_editor_workspace_addin, GBP, EDITOR_WORKSPACE_ADDIN, 
GObject)
+
+G_END_DECLS
diff --git a/src/plugins/editor/gtk/menus.ui b/src/plugins/editor/gtk/menus.ui
new file mode 100644
index 000000000..a521c44f0
--- /dev/null
+++ b/src/plugins/editor/gtk/menus.ui
@@ -0,0 +1,171 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <menu id="ide-primary-workspace-menu">
+    <section id="ide-primary-workspace-menu-placeholder1">
+      <item>
+        <attribute name="id">ide-primary-workspace-menu-new-editor-workspace</attribute>
+        <attribute name="label" translatable="yes">New Workspace…</attribute>
+        <attribute name="action">win.new-editor-workspace</attribute>
+      </item>
+    </section>
+  </menu>
+  <menu id="ide-editor-workspace-menu">
+    <section id="ide-editor-workspace-menu-projects-section"/>
+    <section id="ide-editor-workspace-menu-placeholder1">
+      <item>
+        <attribute name="id">ide-editor-workspace-menu-new-editor-workspace</attribute>
+        <attribute name="label" translatable="yes">New Workspace…</attribute>
+        <attribute name="action">win.new-editor-workspace</attribute>
+      </item>
+    </section>
+    <section id="ide-editor-workspace-menu-open-section">
+      <item>
+        <attribute name="id">ide-editor-workspace-menu-open</attribute>
+        <attribute name="label" translatable="yes">Open File…</attribute>
+        <attribute name="action">workbench.open</attribute>
+        <attribute name="accel">&lt;primary&gt;o</attribute>
+      </item>
+    </section>
+    <section id="ide-editor-workspace-menu-app-section">
+      <item>
+        <attribute name="id">ide-editor-workspace-menu-preferences</attribute>
+        <attribute name="label" translatable="yes">Preferences</attribute>
+        <attribute name="action">app.preferences</attribute>
+        <attribute name="accel">&lt;primary&gt;comma</attribute>
+      </item>
+      <item>
+        <attribute name="id">ide-editor-workspace-menu-shortcuts</attribute>
+        <attribute name="label" translatable="yes">Keyboard Shortcuts</attribute>
+        <attribute name="action">app.shortcuts</attribute>
+        <attribute name="accel">&lt;primary&gt;question</attribute>
+      </item>
+      <item>
+        <attribute name="id">ide-editor-workspace-menu-help</attribute>
+        <attribute name="label" translatable="yes">Help</attribute>
+        <attribute name="action">app.help</attribute>
+        <attribute name="accel">F1</attribute>
+      </item>
+      <item>
+        <attribute name="id">ide-editor-workspace-menu-about</attribute>
+        <attribute name="label" translatable="yes">About Builder</attribute>
+        <attribute name="action">app.about</attribute>
+      </item>
+    </section>
+    <section id="ide-editor-workspace-menu-quit-section">
+      <item>
+        <attribute name="id">ide-editor-workspace-menu-quit</attribute>
+        <attribute name="label" translatable="yes">_Quit</attribute>
+        <attribute name="action">app.quit</attribute>
+      </item>
+    </section>
+  </menu>
+  <menu id="ide-primary-workspace-surfaces-menu">
+    <section id="ide-primary-workspace-surfaces-menu-section">
+      <item>
+        <attribute name="accel">&lt;alt&gt;1</attribute>
+        <attribute name="id">ide-primary-workspace-menu-surfaces-menu-editor</attribute>
+        <attribute name="label" translatable="yes">Editor</attribute>
+        <attribute name="role">normal</attribute>
+        <attribute name="action">win.surface</attribute>
+        <attribute name="target">editor</attribute>
+        <attribute name="verb-icon-name">builder-editor-symbolic</attribute>
+      </item>
+    </section>
+  </menu>
+  <menu id="ide-editor-workspace-surfaces-menu">
+    <section id="ide-editor-workspace-surfaces-menu-section">
+      <attribute name="label" translatable="yes">Switch Surface</attribute>
+      <item>
+        <attribute name="accel">&lt;alt&gt;1</attribute>
+        <attribute name="id">ide-primary-workspace-menu-surfaces-menu-editor</attribute>
+        <attribute name="label" translatable="yes">Editor</attribute>
+        <attribute name="role">normal</attribute>
+        <attribute name="action">win.surface</attribute>
+        <attribute name="target">editor</attribute>
+        <attribute name="verb-icon-name">builder-editor-symbolic</attribute>
+      </item>
+    </section>
+  </menu>
+  <menu id="new-document-menu">
+    <section id="new-document-section">
+      <item>
+        <attribute name="id">new-file</attribute>
+        <attribute name="label" translatable="yes">New File</attribute>
+        <attribute name="action">editor.new-file</attribute>
+      </item>
+    </section>
+    <section id="open-document-section">
+      <item>
+        <attribute name="id">open-file</attribute>
+        <attribute name="label" translatable="yes">Open File…</attribute>
+        <attribute name="action">workbench.open</attribute>
+      </item>
+    </section>
+  </menu>
+  <menu id="ide-frame-menu">
+    <section id="ide-frame-section">
+      <attribute name="label" translatable="yes">Frame</attribute>
+      <item>
+        <attribute name="label" translatable="yes">Move Left</attribute>
+        <attribute name="action">frame.move-left</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">Move Right</attribute>
+        <attribute name="action">frame.move-right</attribute>
+      </item>
+      <item>
+        <attribute name="action">grid.close-stack</attribute>
+        <attribute name="label" translatable="yes">Close</attribute>
+      </item>
+    </section>
+  </menu>
+  <menu id="ide-editor-page-document-menu">
+    <section id="editor-document-section">
+      <attribute name="label" translatable="yes">Document</attribute>
+      <item>
+        <attribute name="id">editor-document-open-in-new-frame</attribute>
+        <attribute name="label" translatable="yes">Open in New Frame</attribute>
+        <attribute name="action">frame.open-in-new-frame</attribute>
+        <attribute name="target" type="s">""</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">Split</attribute>
+        <attribute name="action">frame.split-page</attribute>
+        <attribute name="target" type="s">""</attribute>
+        <attribute name="verb-icon-name">builder-split-tab-symbolic</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">Print…</attribute>
+        <attribute name="action">editor-page.print</attribute>
+        <attribute name="accel">&lt;primary&gt;p</attribute>
+      </item>
+    </section>
+    <section id="editor-document-preferences-section">
+      <attribute name="after">editor-document-section</attribute>
+      <item>
+        <attribute name="label" translatable="yes">Document Properties</attribute>
+        <attribute name="action">editor-page.properties</attribute>
+      </item>
+    </section>
+    <section id="editor-document-save-section">
+      <attribute name="after">editor-document-preferences-section</attribute>
+      <item>
+        <attribute name="action">editor-page.save</attribute>
+        <attribute name="label" translatable="yes">_Save</attribute>
+        <attribute name="accel">&lt;primary&gt;s</attribute>
+      </item>
+      <item>
+        <attribute name="action">editor-page.save-as</attribute>
+        <attribute name="label" translatable="yes">Save _As</attribute>
+        <attribute name="accel">&lt;primary&gt;&lt;shift&gt;s</attribute>
+      </item>
+    </section>
+    <section id="editor-document-close-section">
+      <attribute name="after">editor-document-save-section</attribute>
+      <item>
+        <attribute name="action">frame.close-page</attribute>
+        <attribute name="label" translatable="yes">Close</attribute>
+      </item>
+    </section>
+  </menu>
+</interface>
diff --git a/src/plugins/editor/meson.build b/src/plugins/editor/meson.build
new file mode 100644
index 000000000..7c0101be7
--- /dev/null
+++ b/src/plugins/editor/meson.build
@@ -0,0 +1,18 @@
+plugins_sources += files([
+  'editor-plugin.c',
+  'gbp-editor-application-addin.c',
+  'gbp-editor-frame-addin.c',
+  'gbp-editor-frame-controls.c',
+  'gbp-editor-hover-provider.c',
+  'gbp-editor-session-addin.c',
+  'gbp-editor-workbench-addin.c',
+  'gbp-editor-workspace-addin.c',
+])
+
+plugin_editor_resources = gnome.compile_resources(
+  'gbp-editor-resources',
+  'editor.gresource.xml',
+  c_name: 'gbp_editor',
+)
+
+plugins_sources += plugin_editor_resources[0]
diff --git a/src/plugins/editor/shared.css b/src/plugins/editor/shared.css
new file mode 100644
index 000000000..88786103a
--- /dev/null
+++ b/src/plugins/editor/shared.css
@@ -0,0 +1,24 @@
+@binding-set builder-command-bar-entry
+{
+  bind "<ctrl>a" { "move-cursor" (paragraph-ends, -1, 0) };
+  bind "<ctrl>e" { "move-cursor" (paragraph-ends, 1, 0) };
+
+  bind "<ctrl>u" { "move-cursor" (paragraph-ends, -1, 0)
+                   "delete-from-cursor" (paragraph-ends, 1) };
+  bind "<ctrl>h" { "delete-from-cursor" (chars, -1) };
+  bind "<ctrl>w" { "delete-from-cursor" (word-ends, -1) };
+
+}
+
+@binding-set builder-workbench-bindings
+{
+  bind "<ctrl>comma" { "action" ("app", "preferences", "") };
+}
+
+entry.gb-command-bar-entry {
+  -gtk-key-bindings: builder-command-bar-entry;
+}
+
+window.workbench {
+  -gtk-key-bindings: builder-workbench-bindings;
+}



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