[gnome-builder] documentation-card: card showing relevant information about a library function



commit 59e527d41ce979f79e1f29091c66707986cad97e
Author: Lucie Dvorakova <luci_dvorak me com>
Date:   Tue Aug 15 20:03:00 2017 +0000

    documentation-card: card showing relevant information about a library function
    
    https://bugzilla.gnome.org/show_bug.cgi?id=785854

 libide/documentation/ide-documentation-info.c      |  221 +++++++++++++
 libide/documentation/ide-documentation-info.h      |   48 +++
 libide/documentation/ide-documentation-proposal.c  |  233 ++++++++++++++
 libide/documentation/ide-documentation-proposal.h  |   49 +++
 libide/documentation/ide-documentation-provider.c  |   53 +++
 libide/documentation/ide-documentation-provider.h  |   52 +++
 libide/documentation/ide-documentation.c           |  115 +++++++
 libide/documentation/ide-documentation.h           |   41 +++
 libide/ide-context.c                               |   52 +++
 libide/ide-context.h                               |    1 +
 libide/ide-types.h                                 |    4 +
 libide/ide.h                                       |    4 +
 libide/meson.build                                 |    8 +
 meson_options.txt                                  |    1 +
 .../devhelp/gbp-devhelp-documentation-provider.c   |  333 ++++++++++++++++++++
 .../devhelp/gbp-devhelp-documentation-provider.h   |   32 ++
 plugins/devhelp/gbp-devhelp-editor-view-addin.c    |    5 +-
 plugins/devhelp/gbp-devhelp-plugin.c               |    4 +
 plugins/devhelp/meson.build                        |    2 +
 .../documentation-card/documentation-card.plugin   |    9 +
 .../gbp-documentation-card-plugin.c                |   30 ++
 .../gbp-documentation-card-resources.gresource.xml |    6 +
 .../gbp-documentation-card-view-addin.c            |  244 ++++++++++++++
 .../gbp-documentation-card-view-addin.h            |   30 ++
 .../documentation-card/gbp-documentation-card.c    |  146 +++++++++
 .../documentation-card/gbp-documentation-card.h    |   36 +++
 .../documentation-card/gbp-documentation-card.ui   |   49 +++
 plugins/documentation-card/meson.build             |   28 ++
 plugins/meson.build                                |    1 +
 29 files changed, 1836 insertions(+), 1 deletions(-)
---
diff --git a/libide/documentation/ide-documentation-info.c b/libide/documentation/ide-documentation-info.c
new file mode 100644
index 0000000..a432667
--- /dev/null
+++ b/libide/documentation/ide-documentation-info.c
@@ -0,0 +1,221 @@
+/* ide-documentation-info.c
+ *
+ * Copyright (C) 2017 Lucie Charvat <luci charvat gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-documentation-info"
+
+#include "ide-documentation-info.h"
+#include "ide-documentation-proposal.h"
+
+struct _IdeDocumentationInfo
+{
+  GObject parent_instance;
+
+  gchar                   *input;
+  IdeDocumentationContext  context;
+  GPtrArray               *proposals;
+};
+
+G_DEFINE_TYPE (IdeDocumentationInfo, ide_documentation_info, G_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_INPUT,
+  PROP_CONTEXT,
+  LAST_PROP
+};
+
+static GParamSpec *properties [LAST_PROP];
+
+IdeDocumentationInfo *
+ide_documentation_info_new (const gchar            *input,
+                            IdeDocumentationContext context)
+{
+
+  return g_object_new (IDE_TYPE_DOCUMENTATION_INFO,
+                       "input", input,
+                       "context", context,
+                       NULL);
+}
+
+void
+ide_documentation_info_take_proposal (IdeDocumentationInfo        *self,
+                                      IdeDocumentationProposal    *proposal)
+{
+  g_return_if_fail (IDE_IS_DOCUMENTATION_INFO (self));
+  g_return_if_fail (IDE_IS_DOCUMENTATION_PROPOSAL (proposal));
+
+  g_ptr_array_add (self->proposals, proposal);
+}
+
+/**
+ * ide_documentation_info_get_proposal:
+ * @self: An #IdeDocumentationInfo
+ * @index: the number of the proposal
+ *
+ * Requests proposal for the index.
+ *
+ * Returns: (transfer none): An #IdeDocumentationProposal
+ */
+
+IdeDocumentationProposal *
+ide_documentation_info_get_proposal (IdeDocumentationInfo *self,
+                                     guint                 index)
+{
+  g_return_val_if_fail (IDE_IS_DOCUMENTATION_INFO (self), NULL);
+  g_return_val_if_fail (self->proposals != NULL, NULL);
+  g_return_val_if_fail (self->proposals->len > index, NULL);
+
+  return g_ptr_array_index (self->proposals, index);
+}
+
+static void
+ide_documentation_info_finalize (GObject *object)
+{
+  IdeDocumentationInfo *self = (IdeDocumentationInfo *)object;
+
+  g_clear_pointer (&self->input, g_free);
+  g_clear_pointer (&self->proposals, g_ptr_array_unref);
+
+  G_OBJECT_CLASS (ide_documentation_info_parent_class)->finalize (object);
+}
+
+gchar *
+ide_documentation_info_get_input (IdeDocumentationInfo *self)
+{
+  g_return_val_if_fail (IDE_IS_DOCUMENTATION_INFO (self), NULL);
+
+  return self->input;
+}
+
+IdeDocumentationContext
+ide_documentation_info_get_context (IdeDocumentationInfo *self)
+{
+  g_return_val_if_fail (IDE_IS_DOCUMENTATION_INFO (self), IDE_DOCUMENTATION_CONTEXT_NONE);
+
+  return self->context;
+}
+
+guint
+ide_documentation_info_get_size (IdeDocumentationInfo *self)
+{
+  g_return_val_if_fail (IDE_IS_DOCUMENTATION_INFO (self), 0);
+
+  return self->proposals != NULL ? self->proposals->len : 0;
+}
+
+static void
+ide_documentation_info_set_input (IdeDocumentationInfo *self,
+                                  const gchar          *input)
+{
+  g_return_if_fail (IDE_IS_DOCUMENTATION_INFO (self));
+  g_return_if_fail (self->input == NULL);
+
+  self->input = g_strdup (input);
+}
+
+static void
+ide_documentation_info_set_context (IdeDocumentationInfo    *self,
+                                    IdeDocumentationContext  context)
+{
+  g_return_if_fail (IDE_IS_DOCUMENTATION_INFO (self));
+  g_return_if_fail (self->context == IDE_DOCUMENTATION_CONTEXT_NONE);
+
+  self->context = context;
+}
+
+static void
+ide_documentation_info_get_property (GObject    *object,
+                                     guint       prop_id,
+                                     GValue     *value,
+                                     GParamSpec *pspec)
+{
+  IdeDocumentationInfo *self = IDE_DOCUMENTATION_INFO (object);
+
+  switch (prop_id)
+    {
+    case PROP_INPUT:
+      g_value_set_string (value, ide_documentation_info_get_input (self));
+      break;
+
+    case PROP_CONTEXT:
+      g_value_set_int (value, ide_documentation_info_get_context (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_documentation_info_set_property (GObject      *object,
+                                     guint         prop_id,
+                                     const GValue *value,
+                                     GParamSpec   *pspec)
+{
+  IdeDocumentationInfo *self = IDE_DOCUMENTATION_INFO (object);
+
+  switch (prop_id)
+    {
+    case PROP_INPUT:
+      ide_documentation_info_set_input (self, g_value_get_string (value));
+      break;
+
+    case PROP_CONTEXT:
+      ide_documentation_info_set_context (self, g_value_get_int (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_documentation_info_class_init (IdeDocumentationInfoClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_documentation_info_finalize;
+  object_class->get_property = ide_documentation_info_get_property;
+  object_class->set_property = ide_documentation_info_set_property;
+
+  properties [PROP_INPUT] =
+    g_param_spec_string ("input",
+                         "Input",
+                         "Input",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_CONTEXT] =
+    g_param_spec_int ("context",
+                      "Context",
+                      "Context",
+                      IDE_DOCUMENTATION_CONTEXT_NONE,
+                      IDE_DOCUMENTATION_CONTEXT_LAST,
+                      IDE_DOCUMENTATION_CONTEXT_NONE,
+                      (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+  g_object_class_install_properties (object_class, LAST_PROP, properties);
+}
+
+static void
+ide_documentation_info_init (IdeDocumentationInfo *self)
+{
+  self->proposals = g_ptr_array_new_with_free_func (g_object_unref);
+  self->context = IDE_DOCUMENTATION_CONTEXT_NONE;
+  self->input = NULL;
+}
diff --git a/libide/documentation/ide-documentation-info.h b/libide/documentation/ide-documentation-info.h
new file mode 100644
index 0000000..1328b2a
--- /dev/null
+++ b/libide/documentation/ide-documentation-info.h
@@ -0,0 +1,48 @@
+/* ide-documentation-info.h
+ *
+ * Copyright (C) 2017 Lucie Charvat <luci charvat gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_DOCUMENTATION_INFO_H
+#define IDE_DOCUMENTATION_INFO_H
+
+#include "ide-documentation-proposal.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_DOCUMENTATION_INFO (ide_documentation_info_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeDocumentationInfo, ide_documentation_info, IDE, DOCUMENTATION_INFO, GObject)
+
+typedef enum {
+  IDE_DOCUMENTATION_CONTEXT_NONE,
+  IDE_DOCUMENTATION_CONTEXT_CARD_C,
+  IDE_DOCUMENTATION_CONTEXT_LAST,
+} IdeDocumentationContext;
+
+IdeDocumentationInfo     *ide_documentation_info_new            (const gchar                 *input,
+                                                                 IdeDocumentationContext      context);
+void                      ide_documentation_info_take_proposal  (IdeDocumentationInfo        *self,
+                                                                 IdeDocumentationProposal    *proposal);
+IdeDocumentationContext   ide_documentation_info_get_context    (IdeDocumentationInfo        *self);
+gchar                    *ide_documentation_info_get_input      (IdeDocumentationInfo        *self);
+guint                     ide_documentation_info_get_size       (IdeDocumentationInfo        *self);
+IdeDocumentationProposal *ide_documentation_info_get_proposal   (IdeDocumentationInfo        *self,
+                                                                 guint                        index);
+
+G_END_DECLS
+
+#endif /* IDE_DOCUMENTATION_INFO_H */
diff --git a/libide/documentation/ide-documentation-proposal.c 
b/libide/documentation/ide-documentation-proposal.c
new file mode 100644
index 0000000..04c9373
--- /dev/null
+++ b/libide/documentation/ide-documentation-proposal.c
@@ -0,0 +1,233 @@
+/* ide-documentation-proposal.c
+ *
+ * Copyright (C) 2017 Lucie Charvat <luci charvat gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-documentation-proposal"
+
+#include "ide-documentation-proposal.h"
+
+typedef struct
+{
+  gchar       *header;
+  gchar       *text;
+  gchar       *uri;
+} IdeDocumentationProposalPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (IdeDocumentationProposal, ide_documentation_proposal, G_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_HEADER,
+  PROP_TEXT,
+  PROP_URI,
+  LAST_PROP
+};
+
+static GParamSpec *properties [LAST_PROP];
+
+IdeDocumentationProposal *
+ide_documentation_proposal_new (const gchar *uri)
+{
+  return g_object_new (IDE_TYPE_DOCUMENTATION_PROPOSAL,
+                       "uri", uri,
+                       NULL);
+}
+
+const gchar *
+ide_documentation_proposal_get_header (IdeDocumentationProposal *self)
+{
+  IdeDocumentationProposalPrivate *priv = ide_documentation_proposal_get_instance_private (self);
+
+  g_return_val_if_fail (IDE_IS_DOCUMENTATION_PROPOSAL (self), NULL);
+
+  return priv->header;
+}
+
+const gchar *
+ide_documentation_proposal_get_text (IdeDocumentationProposal *self)
+{
+  IdeDocumentationProposalPrivate *priv = ide_documentation_proposal_get_instance_private (self);
+
+  g_return_val_if_fail (IDE_IS_DOCUMENTATION_PROPOSAL (self), NULL);
+
+  return priv->text;
+}
+
+const gchar *
+ide_documentation_proposal_get_uri (IdeDocumentationProposal *self)
+{
+  IdeDocumentationProposalPrivate *priv = ide_documentation_proposal_get_instance_private (self);
+
+  g_return_val_if_fail (IDE_IS_DOCUMENTATION_PROPOSAL (self), NULL);
+
+  return priv->uri;
+}
+
+
+void
+ide_documentation_proposal_set_header (IdeDocumentationProposal *self,
+                                       const gchar              *header)
+{
+  IdeDocumentationProposalPrivate *priv = ide_documentation_proposal_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_DOCUMENTATION_PROPOSAL (self));
+
+  if (g_strcmp0 (priv->header, header) != 0)
+    {
+      g_free (priv->header);
+      priv->header = g_strdup (header);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HEADER]);
+    }
+}
+
+void
+ide_documentation_proposal_set_text (IdeDocumentationProposal *self,
+                                     const gchar              *text)
+{
+  IdeDocumentationProposalPrivate *priv = ide_documentation_proposal_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_DOCUMENTATION_PROPOSAL (self));
+
+    if (g_strcmp0 (priv->text, text) != 0)
+    {
+      g_free (priv->text);
+      priv->text = g_strdup (text);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TEXT]);
+    }
+}
+
+void
+ide_documentation_proposal_set_uri  (IdeDocumentationProposal *self,
+                                     const gchar              *uri)
+{
+  IdeDocumentationProposalPrivate *priv = ide_documentation_proposal_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_DOCUMENTATION_PROPOSAL (self));
+
+  priv->uri = g_strdup (uri);
+}
+
+static void
+ide_documentation_proposal_get_property (GObject    *object,
+                                     guint       prop_id,
+                                     GValue     *value,
+                                     GParamSpec *pspec)
+{
+  IdeDocumentationProposal *self = IDE_DOCUMENTATION_PROPOSAL (object);
+
+  switch (prop_id)
+    {
+    case PROP_HEADER:
+      g_value_set_string (value, ide_documentation_proposal_get_header (self));
+      break;
+
+    case PROP_TEXT:
+      g_value_set_string (value, ide_documentation_proposal_get_text (self));
+      break;
+
+    case PROP_URI:
+      g_value_set_string (value, ide_documentation_proposal_get_uri (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_documentation_proposal_set_property (GObject      *object,
+                                         guint         prop_id,
+                                         const GValue *value,
+                                         GParamSpec   *pspec)
+{
+  IdeDocumentationProposal *self = IDE_DOCUMENTATION_PROPOSAL (object);
+
+  switch (prop_id)
+    {
+    case PROP_HEADER:
+      ide_documentation_proposal_set_header (self, g_value_get_string (value));
+      break;
+
+    case PROP_TEXT:
+      ide_documentation_proposal_set_text (self, g_value_get_string (value));
+      break;
+
+    case PROP_URI:
+      ide_documentation_proposal_set_uri (self, g_value_get_string (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_documentation_proposal_finalize (GObject *object)
+{
+  IdeDocumentationProposal *self = (IdeDocumentationProposal *)object;
+  IdeDocumentationProposalPrivate *priv = ide_documentation_proposal_get_instance_private (self);
+
+  g_clear_pointer (&priv->header, g_free);
+  g_clear_pointer (&priv->text, g_free);
+  g_clear_pointer (&priv->uri, g_free);
+
+  G_OBJECT_CLASS (ide_documentation_proposal_parent_class)->finalize (object);
+}
+
+static void
+ide_documentation_proposal_class_init (IdeDocumentationProposalClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_documentation_proposal_finalize;
+  object_class->get_property = ide_documentation_proposal_get_property;
+  object_class->set_property = ide_documentation_proposal_set_property;
+
+  properties [PROP_HEADER] =
+    g_param_spec_string ("header",
+                         "Header",
+                         "Header",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_TEXT] =
+    g_param_spec_string ("text",
+                         "Text",
+                         "Text",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_URI] =
+    g_param_spec_string ("uri",
+                         "Uri",
+                         "Uri",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, LAST_PROP, properties);
+}
+
+void
+ide_documentation_proposal_init (IdeDocumentationProposal *self)
+{
+  IdeDocumentationProposalPrivate *priv = ide_documentation_proposal_get_instance_private (self);
+
+  priv->header = NULL;
+  priv->text = NULL;
+  priv->uri = NULL;
+}
+
diff --git a/libide/documentation/ide-documentation-proposal.h 
b/libide/documentation/ide-documentation-proposal.h
new file mode 100644
index 0000000..015c99e
--- /dev/null
+++ b/libide/documentation/ide-documentation-proposal.h
@@ -0,0 +1,49 @@
+/* ide-documentation-proposal.h
+ *
+ * Copyright (C) 2017 Lucie Charvat <luci charvat gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_DOCUMENTATION_PROPOSAL_H
+#define IDE_DOCUMENTATION_PROPOSAL_H
+
+#include "ide-object.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_DOCUMENTATION_PROPOSAL            (ide_documentation_proposal_get_type())
+
+struct _IdeDocumentationProposalClass
+{
+  GObjectClass parent_class;
+};
+
+typedef struct _IdeDocumentationProposalClass IdeDocumentationProposalClass;
+
+G_DECLARE_DERIVABLE_TYPE (IdeDocumentationProposal, ide_documentation_proposal, IDE, DOCUMENTATION_PROPOSAL, 
GObject)
+
+IdeDocumentationProposal *ide_documentation_proposal_new            (const gchar                *url);
+void                      ide_documentation_proposal_set_header     (IdeDocumentationProposal   *self,
+                                                                     const gchar                *header);
+void                      ide_documentation_proposal_set_text       (IdeDocumentationProposal   *self,
+                                                                     const gchar                *text);
+const gchar              *ide_documentation_proposal_get_header     (IdeDocumentationProposal   *self);
+const gchar              *ide_documentation_proposal_get_text       (IdeDocumentationProposal   *self);
+const gchar              *ide_documentation_proposal_get_uri        (IdeDocumentationProposal   *self);
+
+
+G_END_DECLS
+
+#endif /* IDE_DOCUMENTATION_PROPOSAL_H */
diff --git a/libide/documentation/ide-documentation-provider.c 
b/libide/documentation/ide-documentation-provider.c
new file mode 100644
index 0000000..9132542
--- /dev/null
+++ b/libide/documentation/ide-documentation-provider.c
@@ -0,0 +1,53 @@
+/* ide-documentation-provider.c
+ *
+ * Copyright (C) 2017 Lucie Charvat <luci charvat gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-documentation-provider"
+
+#include "ide-documentation-provider.h"
+
+G_DEFINE_INTERFACE (IdeDocumentationProvider, ide_documentation_provider, IDE_TYPE_OBJECT)
+
+static void
+ide_documentation_provider_default_init (IdeDocumentationProviderInterface *iface)
+{
+}
+
+void
+ide_documentation_provider_get_info (IdeDocumentationProvider *provider,
+                                     IdeDocumentationInfo     *info)
+{
+  g_return_if_fail (IDE_IS_DOCUMENTATION_PROVIDER (provider));
+
+  return IDE_DOCUMENTATION_PROVIDER_GET_IFACE (provider)->get_info (provider, info);
+}
+
+gchar *
+ide_documentation_provider_get_name (IdeDocumentationProvider *provider)
+{
+  g_return_val_if_fail (IDE_IS_DOCUMENTATION_PROVIDER (provider), NULL);
+
+  return IDE_DOCUMENTATION_PROVIDER_GET_IFACE (provider)->get_name (provider);
+}
+
+IdeDocumentationContext
+ide_documentation_provider_get_context (IdeDocumentationProvider *provider)
+{
+  g_return_val_if_fail (IDE_IS_DOCUMENTATION_PROVIDER (provider), IDE_DOCUMENTATION_CONTEXT_NONE);
+
+  return IDE_DOCUMENTATION_PROVIDER_GET_IFACE (provider)->get_context (provider);
+}
diff --git a/libide/documentation/ide-documentation-provider.h 
b/libide/documentation/ide-documentation-provider.h
new file mode 100644
index 0000000..bf426fd
--- /dev/null
+++ b/libide/documentation/ide-documentation-provider.h
@@ -0,0 +1,52 @@
+/* ide-documentation-provider.h
+ *
+ * Copyright (C) 2017 Lucie Charvat <luci charvat gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_DOCUMENTATION_PROVIDER_H
+#define IDE_DOCUMENTATION_PROVIDER_H
+
+#include <gtksourceview/gtksource.h>
+
+#include "ide-documentation-info.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_DOCUMENTATION_PROVIDER             (ide_documentation_provider_get_type())
+
+G_DECLARE_INTERFACE (IdeDocumentationProvider, ide_documentation_provider, IDE, DOCUMENTATION_PROVIDER, 
IdeObject)
+
+struct _IdeDocumentationProviderInterface
+{
+  GTypeInterface       parent_interface;
+
+  void                    (*get_info)        (IdeDocumentationProvider    *self,
+                                              IdeDocumentationInfo        *info);
+  gchar                  *(*get_name)        (IdeDocumentationProvider    *self);
+  IdeDocumentationContext (*get_context)     (IdeDocumentationProvider    *self);
+};
+
+gchar                  *ide_documentation_provider_get_name          (IdeDocumentationProvider    *self);
+void                    ide_documentation_provider_get_info          (IdeDocumentationProvider    *self,
+                                                                      IdeDocumentationInfo        *info);
+IdeDocumentationContext ide_documentation_provider_get_context       (IdeDocumentationProvider    *self);
+
+
+G_END_DECLS
+
+#endif /* IDE_DOCUMENTATION_PROVIDER_H */
+
+
diff --git a/libide/documentation/ide-documentation.c b/libide/documentation/ide-documentation.c
new file mode 100644
index 0000000..97dea06
--- /dev/null
+++ b/libide/documentation/ide-documentation.c
@@ -0,0 +1,115 @@
+/* ide-documentation.c
+ *
+ * Copyright (C) 2017 Lucie Charvat <luci charvat gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+#include <libpeas/peas.h>
+
+#include "ide-documentation.h"
+#include "ide-documentation-provider.h"
+#include "ide-documentation-proposal.h"
+
+struct _IdeDocumentation
+{
+  GObject                  parent_instance;
+  PeasExtensionSet        *extensions;
+};
+
+G_DEFINE_TYPE (IdeDocumentation, ide_documentation, IDE_TYPE_OBJECT)
+
+static void
+ide_documentation_finalize (GObject *object)
+{
+  IdeDocumentation *self = (IdeDocumentation *)object;
+
+  g_clear_object (&self->extensions);
+
+  G_OBJECT_CLASS (ide_documentation_parent_class)->finalize (object);
+}
+
+static void
+ide_documentation_search_foreach (PeasExtensionSet *set,
+                                  PeasPluginInfo   *plugin_info,
+                                  PeasExtension    *exten,
+                                  gpointer          user_data)
+{
+  IdeDocumentationProvider *provider = (IdeDocumentationProvider *) exten;
+  IdeDocumentationInfo *info = user_data;
+
+  if (ide_documentation_provider_get_context (provider) == ide_documentation_info_get_context (info))
+    ide_documentation_provider_get_info (provider, info);
+}
+
+static void
+ide_documentation_constructed (GObject *object)
+{
+  IdeDocumentation *self = (IdeDocumentation *)object;
+  IdeContext *context;
+
+  g_assert (IDE_IS_DOCUMENTATION (self));
+
+  G_OBJECT_CLASS (ide_documentation_parent_class)->constructed (object);
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+
+  self->extensions = peas_extension_set_new (peas_engine_get_default (),
+                                             IDE_TYPE_DOCUMENTATION_PROVIDER,
+                                             "context", context,
+                                             NULL);
+}
+
+static void
+ide_documentation_class_init (IdeDocumentationClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_documentation_finalize;
+  object_class->constructed = ide_documentation_constructed;
+}
+
+static void
+ide_documentation_init (IdeDocumentation *self)
+{
+}
+
+/**
+ * ide_documentation_get_info:
+ * @self: An #IdeDocumentation
+ * @input: the search keyword
+ * @context: the context for the request
+ *
+ * Requests documentation for the keyword.
+ *
+ * Returns: (transfer full): An #IdeDocumentationInfo
+ */
+IdeDocumentationInfo *
+ide_documentation_get_info    (IdeDocumentation        *self,
+                               const gchar             *input,
+                               IdeDocumentationContext  context)
+{
+  IdeDocumentationInfo *info;
+
+  g_assert (IDE_IS_DOCUMENTATION (self));
+  g_return_val_if_fail (input != NULL, NULL);
+
+  info = ide_documentation_info_new (input, context);
+
+  peas_extension_set_foreach (self->extensions,
+                              ide_documentation_search_foreach,
+                              info);
+  return info;
+}
diff --git a/libide/documentation/ide-documentation.h b/libide/documentation/ide-documentation.h
new file mode 100644
index 0000000..2a6c572
--- /dev/null
+++ b/libide/documentation/ide-documentation.h
@@ -0,0 +1,41 @@
+/* ide-documentation.h
+ *
+ * Copyright (C) 2017 Lucie Charvat <luci charavt gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_DOCUMENTATION_H
+#define IDE_DOCUMENTATION_H
+
+#include <gtksourceview/gtksource.h>
+
+#include "ide-documentation-info.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_DOCUMENTATION (ide_documentation_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeDocumentation,
+                      ide_documentation,
+                      IDE, DOCUMENTATION,
+                      IdeObject)
+
+IdeDocumentationInfo    *ide_documentation_get_info    (IdeDocumentation        *self,
+                                                        const gchar             *input,
+                                                        IdeDocumentationContext  context);
+
+G_END_DECLS
+
+#endif /* IDE_DOCUMENTATION_H */
diff --git a/libide/ide-context.c b/libide/ide-context.c
index b256217..0f1166e 100644
--- a/libide/ide-context.c
+++ b/libide/ide-context.c
@@ -50,6 +50,7 @@
 #include "search/ide-search-engine.h"
 #include "search/ide-search-provider.h"
 #include "snippets/ide-source-snippets-manager.h"
+#include "documentation/ide-documentation.h"
 #include "transfers/ide-transfer-manager.h"
 #include "util/ide-async-helper.h"
 #include "util/ide-settings.h"
@@ -71,6 +72,7 @@ struct _IdeContext
   IdeDiagnosticsManager    *diagnostics_manager;
   IdeDeviceManager         *device_manager;
   IdeDoap                  *doap;
+  IdeDocumentation         *documentation;
   GtkRecentManager         *recent_manager;
   IdeRunManager            *run_manager;
   IdeRuntimeManager        *runtime_manager;
@@ -108,6 +110,7 @@ enum {
   PROP_BUILD_SYSTEM,
   PROP_CONFIGURATION_MANAGER,
   PROP_DEVICE_MANAGER,
+  PROP_DOCUMENTATION,
   PROP_PROJECT_FILE,
   PROP_PROJECT,
   PROP_ROOT_BUILD_DIR,
@@ -227,6 +230,23 @@ ide_context_get_device_manager (IdeContext *self)
 }
 
 /**
+ * ide_context_get_documentation:
+ * @self: An #IdeContext.
+ *
+ * Returns the #IdeDocumentation for the source view if there is one.
+ *
+ * Returns: (transfer none) (nullable): A #IdeDocumentation or %NULL.
+ */
+IdeDocumentation *
+ide_context_get_documentation (IdeContext *self)
+{
+  g_return_val_if_fail (IDE_IS_CONTEXT (self), NULL);
+
+  return self->documentation;
+}
+
+
+/**
  * ide_context_get_root_build_dir:
  *
  * Retrieves the "root-build-dir" for the context. This is the root directory
@@ -602,6 +622,10 @@ ide_context_get_property (GObject    *object,
       g_value_set_object (value, ide_context_get_device_manager (self));
       break;
 
+    case PROP_DOCUMENTATION:
+      g_value_set_object (value, ide_context_get_documentation (self));
+      break;
+
     case PROP_PROJECT:
       g_value_set_object (value, ide_context_get_project (self));
       break;
@@ -707,6 +731,13 @@ ide_context_class_init (IdeContextClass *klass)
                          IDE_TYPE_DEVICE_MANAGER,
                          (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
+  properties [PROP_DOCUMENTATION] =
+    g_param_spec_object ("documentation",
+                         "Documentation",
+                         "The documentation for the context.",
+                         IDE_TYPE_DOCUMENTATION,
+                         (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
   properties [PROP_PROJECT] =
     g_param_spec_object ("project",
                          "Project",
@@ -1438,6 +1469,26 @@ ide_context_init_search_engine (gpointer             source_object,
 }
 
 static void
+ide_context_init_documentation (gpointer             source_object,
+                                GCancellable        *cancellable,
+                                GAsyncReadyCallback  callback,
+                                gpointer             user_data)
+{
+  g_autoptr(GTask) task = NULL;
+  IdeContext *self = source_object;
+
+  g_assert (IDE_IS_CONTEXT (self));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  self->documentation = g_object_new (IDE_TYPE_DOCUMENTATION,
+                                     "context", self,
+                                      NULL);
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_return_boolean (task, TRUE);
+}
+
+static void
 ide_context_init_configuration_manager_cb (GObject      *object,
                                            GAsyncResult *result,
                                            gpointer      user_data)
@@ -1703,6 +1754,7 @@ ide_context_init_async (GAsyncInitable      *initable,
                         ide_context_init_unsaved_files,
                         ide_context_init_add_recent,
                         ide_context_init_search_engine,
+                        ide_context_init_documentation,
                         ide_context_init_runtimes,
                         ide_context_init_configuration_manager,
                         ide_context_init_build_manager,
diff --git a/libide/ide-context.h b/libide/ide-context.h
index c180226..7c6c50f 100644
--- a/libide/ide-context.h
+++ b/libide/ide-context.h
@@ -38,6 +38,7 @@ IdeBuildSystem           *ide_context_get_build_system          (IdeContext
 IdeConfigurationManager  *ide_context_get_configuration_manager (IdeContext           *self);
 IdeDiagnosticsManager    *ide_context_get_diagnostics_manager   (IdeContext           *self);
 IdeDeviceManager         *ide_context_get_device_manager        (IdeContext           *self);
+IdeDocumentation         *ide_context_get_documentation         (IdeContext           *self);
 IdeProject               *ide_context_get_project               (IdeContext           *self);
 GtkRecentManager         *ide_context_get_recent_manager        (IdeContext           *self);
 IdeRunManager            *ide_context_get_run_manager           (IdeContext           *self);
diff --git a/libide/ide-types.h b/libide/ide-types.h
index 4d5fde3..9a02b4a 100644
--- a/libide/ide-types.h
+++ b/libide/ide-types.h
@@ -56,6 +56,10 @@ typedef struct _IdeDiagnosticProvider          IdeDiagnosticProvider;
 typedef struct _IdeDiagnostics                 IdeDiagnostics;
 typedef struct _IdeDiagnosticsManager          IdeDiagnosticsManager;
 
+typedef struct _IdeDocumentation               IdeDocumentation;
+typedef struct _IdeDocumentationInfo           IdeDocumentationInfo;
+typedef struct _IdeDocumentationProposal       IdeDocumentationProposal;
+
 typedef struct _IdeEnvironment                 IdeEnvironment;
 typedef struct _IdeEnvironmentVariable         IdeEnvironmentVariable;
 
diff --git a/libide/ide.h b/libide/ide.h
index ec7d96c..8d2d512 100644
--- a/libide/ide.h
+++ b/libide/ide.h
@@ -73,6 +73,10 @@ G_BEGIN_DECLS
 #include "diagnostics/ide-source-range.h"
 #include "doap/ide-doap-person.h"
 #include "doap/ide-doap.h"
+#include "documentation/ide-documentation.h"
+#include "documentation/ide-documentation-info.h"
+#include "documentation/ide-documentation-proposal.h"
+#include "documentation/ide-documentation-provider.h"
 #include "editor/ide-editor-addin.h"
 #include "editor/ide-editor-perspective.h"
 #include "editor/ide-editor-sidebar.h"
diff --git a/libide/meson.build b/libide/meson.build
index b9a4902..58f1b1f 100644
--- a/libide/meson.build
+++ b/libide/meson.build
@@ -99,6 +99,10 @@ libide_public_headers = [
   'directory/ide-directory-vcs.h',
   'doap/ide-doap-person.h',
   'doap/ide-doap.h',
+  'documentation/ide-documentation.h',
+  'documentation/ide-documentation-info.h',
+  'documentation/ide-documentation-proposal.h',
+  'documentation/ide-documentation-provider.h',
   'editor/ide-editor-addin.h',
   'editor/ide-editor-perspective.h',
   'editor/ide-editor-sidebar.h',
@@ -297,6 +301,10 @@ libide_public_sources = [
   'directory/ide-directory-vcs.c',
   'doap/ide-doap-person.c',
   'doap/ide-doap.c',
+  'documentation/ide-documentation.c',
+  'documentation/ide-documentation-info.c',
+  'documentation/ide-documentation-proposal.c',
+  'documentation/ide-documentation-provider.c',
   'editor/ide-editor-addin.c',
   'editor/ide-editor-perspective.c',
   'editor/ide-editor-sidebar.c',
diff --git a/meson_options.txt b/meson_options.txt
index 6e77022..38ac26c 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -33,6 +33,7 @@ option('with_command_bar', type: 'boolean')
 option('with_comment_code', type: 'boolean')
 option('with_create_project', type: 'boolean')
 option('with_ctags', type: 'boolean')
+option('with_documentation_card', type: 'boolean')
 option('with_devhelp', type: 'boolean')
 option('with_eslint', type: 'boolean')
 option('with_file_search', type: 'boolean')
diff --git a/plugins/devhelp/gbp-devhelp-documentation-provider.c 
b/plugins/devhelp/gbp-devhelp-documentation-provider.c
new file mode 100644
index 0000000..aa420e3
--- /dev/null
+++ b/plugins/devhelp/gbp-devhelp-documentation-provider.c
@@ -0,0 +1,333 @@
+/* gbp-devhelp-documentation-provider.c
+ *
+ * Copyright (C) 2017 Lucie Charvat <luci charvat gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "devhelp-documentation-provider"
+
+#include <ide.h>
+#include <devhelp/devhelp.h>
+
+#include "gbp-devhelp-documentation-provider.h"
+
+struct _GbpDevhelpDocumentationProvider
+{
+  GObject                       parent_instance;
+
+  IdeDocumentation             *documentation;
+  IdeDocumentationContext       context;
+  IdeDocumentationProposal     *proposal;
+  DhKeywordModel               *keyword_model;
+  gchar                        *uri;
+
+};
+
+static void gbp_devhelp_documentation_provider_interface_init (IdeDocumentationProviderInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GbpDevhelpDocumentationProvider,
+                        gbp_devhelp_documentation_provider,
+                        IDE_TYPE_OBJECT,
+                        0,
+                        G_IMPLEMENT_INTERFACE (IDE_TYPE_DOCUMENTATION_PROVIDER,
+                                               gbp_devhelp_documentation_provider_interface_init))
+
+enum
+{
+  START_HEADER,
+  END_HEADER,
+  END_TEXT,
+
+  REMOVE_TAG_HEADER,
+  REMOVE_TAG_TEXT,
+  REMOVE_MULTI_SPACES,
+  NEW_LINE,
+  NEW_PARAGRAPH,
+  MAKE_BOLD_START,
+  MAKE_BOLD_END,
+  MAKE_BOLD_START_NEW_LINE,
+  MAKE_BOLD_END_NEW_LINE,
+  MAKE_POINT_NEW_LINE,
+  INFORMAL_EXAMPLE,
+  INFORMAL_EXAMPLE_END,
+  CLEAN_UP,
+
+  N_REGEXES
+};
+
+static GRegex *regexes [N_REGEXES];
+
+static gchar*
+regex_replace_line (GRegex      *regex,
+                    gchar       *line,
+                    const gchar *replace)
+{
+  gchar *tmp_line;
+
+  tmp_line = g_regex_replace (regex, line, -1, 0, replace, 0, NULL);
+  g_free (line);
+  return tmp_line;
+}
+
+static gboolean
+xml_parse (GbpDevhelpDocumentationProvider *self,
+           gchar                           *file_name,
+           gchar                           *func_name,
+           IdeDocumentationInfo            *info)
+{
+  GFileInputStream *file_stream;
+  GDataInputStream *data_stream;
+  g_autoptr(GString) header = g_string_new (NULL);
+  g_autoptr(GString) text = g_string_new (NULL);
+  g_autoptr(GError) error_file = NULL;
+  g_autoptr(GError) error_stream = NULL;
+  GRegex *start_text;
+
+  const gchar *regex_char;
+  gboolean informal_example_bool = FALSE;
+  gboolean found_tag = FALSE;
+  gboolean text_tag = FALSE;
+
+  file_stream = g_file_read (g_file_new_for_uri (file_name), NULL, &error_file);
+  if (file_stream == NULL)
+    return FALSE;
+
+  regex_char = g_strdup_printf ("name=\"%s\"", func_name);
+  start_text = g_regex_new (regex_char, 0, 0, NULL);
+
+  data_stream = g_data_input_stream_new (G_INPUT_STREAM (file_stream));
+
+  while (TRUE)
+    {
+      g_autofree gchar *line = NULL;
+      line = g_data_input_stream_read_line_utf8 (data_stream, NULL, NULL, &error_stream);
+      if (line == NULL)
+        return FALSE;
+      if (!found_tag)
+        {
+          if (g_regex_match (start_text, line, 0, NULL))
+            found_tag = TRUE;
+        }
+      if (found_tag && !text_tag)
+        {
+          line = regex_replace_line (regexes[START_HEADER], line, "<tt>");
+          line = regex_replace_line (regexes[REMOVE_TAG_HEADER], line, "");
+          line = regex_replace_line (regexes[MAKE_BOLD_START], line, "<b>");
+          line = regex_replace_line (regexes[MAKE_BOLD_END], line, "</b>");
+          line = regex_replace_line (regexes[NEW_LINE], line, "\n");
+
+          if (g_regex_match (regexes[REMOVE_MULTI_SPACES], line, 0, NULL))
+            continue;
+
+          if (g_regex_match (regexes[END_HEADER], line, 0, NULL))
+            {
+              line = regex_replace_line (regexes[END_HEADER], line, "</tt>");
+              g_string_append (header, line);
+              text_tag = TRUE;
+              continue;
+            }
+          line = regex_replace_line (regexes[CLEAN_UP], line, "\n");
+          g_string_append_printf (header, "%s\n", line);
+        }
+      if (text_tag)
+        {
+          if (g_regex_match (regexes[INFORMAL_EXAMPLE], line, 0, NULL))
+            {
+              informal_example_bool = TRUE;
+              continue;
+            }
+          if (g_regex_match (regexes[INFORMAL_EXAMPLE_END], line, 0, NULL))
+            {
+              informal_example_bool = FALSE;
+              continue;
+            }
+          line = regex_replace_line (regexes[NEW_PARAGRAPH], line, "\t");
+          line = regex_replace_line (regexes[REMOVE_TAG_TEXT], line, "");
+          line = regex_replace_line (regexes[MAKE_BOLD_START], line, "<b>");
+          line = regex_replace_line (regexes[MAKE_BOLD_END], line, "</b>");
+          line = regex_replace_line (regexes[MAKE_BOLD_START_NEW_LINE], line, "\n<b>");
+          line = regex_replace_line (regexes[MAKE_BOLD_END_NEW_LINE], line, "</b>\n");
+          line = regex_replace_line (regexes[MAKE_POINT_NEW_LINE], line, " - ");
+
+          if (g_regex_match (regexes[REMOVE_MULTI_SPACES], line, 0, NULL))
+            continue;
+
+          line = regex_replace_line (regexes[NEW_LINE], line, "\n");
+
+          if (g_regex_match (regexes[END_TEXT], line, 0, NULL))
+            break;
+
+          line = regex_replace_line (regexes[CLEAN_UP], line, "\n");
+
+          if (informal_example_bool)
+            g_string_append_printf (text, "\n<tt>%s</tt>", line);
+          else
+            g_string_append_printf (text, "%s ", line);
+        }
+    }
+
+  self->proposal = ide_documentation_proposal_new (self->uri);
+  ide_documentation_proposal_set_header (self->proposal, header->str);
+  ide_documentation_proposal_set_text (self->proposal, text->str);
+
+  g_string_free (g_steal_pointer (&header), TRUE);
+  g_string_free (g_steal_pointer (&text), TRUE);
+
+  return TRUE;
+}
+
+static gboolean
+get_devhelp_book (GbpDevhelpDocumentationProvider *self,
+                  IdeDocumentationInfo            *info)
+{
+  DhLink *link;
+
+  if (ide_documentation_info_get_input (info) == NULL)
+    return FALSE;
+
+  link = dh_keyword_model_filter (self->keyword_model, ide_documentation_info_get_input (info), NULL, NULL);
+  if (link == NULL)
+    return FALSE;
+
+  g_free (self->uri);
+  self->uri = dh_link_get_uri (link);
+
+  return TRUE;
+}
+
+gchar *
+gbp_devhelp_documentation_provider_get_name (IdeDocumentationProvider *provider)
+{
+  return g_strdup ("Devhelp");
+}
+
+gboolean
+start_get_info (IdeDocumentationProvider *provider,
+                IdeDocumentationInfo     *info)
+{
+  GbpDevhelpDocumentationProvider *self = (GbpDevhelpDocumentationProvider *)provider;
+  g_auto(GStrv) tokens = NULL;
+
+  g_assert (GBP_IS_DEVHELP_DOCUMENTATION_PROVIDER (self));
+
+  if (!get_devhelp_book (self, info))
+    return FALSE;
+
+  tokens = g_strsplit (self->uri, "#", -1 );
+  g_clear_pointer (&self->uri, g_free);
+
+  if (tokens == NULL || g_strv_length (tokens) != 2)
+    return FALSE;
+
+  return xml_parse (self, tokens[0], tokens[1], info);
+}
+
+void
+gbp_devhelp_documentation_provider_get_info (IdeDocumentationProvider *provider,
+                                             IdeDocumentationInfo     *info)
+{
+  GbpDevhelpDocumentationProvider *self = (GbpDevhelpDocumentationProvider *)provider;
+  gboolean parse_succ;
+
+  g_assert (GBP_IS_DEVHELP_DOCUMENTATION_PROVIDER (self));
+
+  parse_succ = start_get_info (provider, info);
+
+  if (parse_succ)
+    {
+      if (G_UNLIKELY (self->proposal == NULL))
+          return;
+
+      ide_documentation_info_take_proposal (info, g_steal_pointer (&self->proposal));
+    }
+}
+
+IdeDocumentationContext
+gbp_devhelp_documentation_provider_get_context (IdeDocumentationProvider *provider)
+{
+  GbpDevhelpDocumentationProvider *self = (GbpDevhelpDocumentationProvider *)provider;
+
+  return self->context;
+}
+
+static void
+gbp_devhelp_documentation_provider_constructed (GObject *object)
+{
+  GbpDevhelpDocumentationProvider *self = (GbpDevhelpDocumentationProvider *)object;
+  IdeContext *context;
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  self->documentation = ide_context_get_documentation (context);
+  self->keyword_model = dh_keyword_model_new ();
+  self->context = IDE_DOCUMENTATION_CONTEXT_CARD_C;
+}
+
+static void
+gbp_devhelp_documentation_provider_finalize (GObject *object)
+{
+  GbpDevhelpDocumentationProvider *self = (GbpDevhelpDocumentationProvider *)object;
+
+  g_clear_object (&self->keyword_model);
+
+  G_OBJECT_CLASS (gbp_devhelp_documentation_provider_parent_class)->finalize (object);
+}
+
+static void
+gbp_devhelp_documentation_provider_class_init (GbpDevhelpDocumentationProviderClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gbp_devhelp_documentation_provider_finalize;
+  object_class->constructed = gbp_devhelp_documentation_provider_constructed;
+
+  regexes[START_HEADER] = g_regex_new (".*<pre.*?>", 0, 0, NULL);
+  regexes[END_HEADER] = g_regex_new ("</pre.*", 0, 0, NULL);
+  regexes[END_TEXT] = g_regex_new ("<hr>", 0, 0, NULL);
+
+  regexes[REMOVE_TAG_HEADER] = g_regex_new ("<p.*?>|</?[ace].*?>|</?ta.*?>|<h3.*/h3>", 0, 0, NULL);
+  regexes[REMOVE_TAG_TEXT] = g_regex_new 
("<p.*?>|</?[cdelsu].*?>|</?t[dba].*?>|</?ac.*?>|</?pre.*?>|\\s*</?td.*?>", 0, 0, NULL);
+  regexes[REMOVE_MULTI_SPACES] = g_regex_new ("^\\s*$|^[\\d|\\s]*$", 0, 0, NULL);
+  regexes[NEW_LINE] = g_regex_new ("</tr>|</p>", 0, 0, NULL);
+  regexes[NEW_PARAGRAPH] = g_regex_new ("</p></td>", 0, 0, NULL);
+  regexes[MAKE_BOLD_START] = g_regex_new ("<a.*?>|<span.*?>", 0, 0, NULL);
+  regexes[MAKE_BOLD_END] = g_regex_new ("</a>|</span>", 0, 0, NULL);
+  regexes[MAKE_BOLD_START_NEW_LINE] = g_regex_new ("<h4.*?>", 0, 0, NULL);
+  regexes[MAKE_BOLD_END_NEW_LINE] = g_regex_new ("</h4>", 0, 0, NULL);
+  regexes[MAKE_POINT_NEW_LINE] = g_regex_new ("<li.*?>|<tr>", 0, 0, NULL);
+  regexes[INFORMAL_EXAMPLE] = g_regex_new ("<div class=\"informalexample\">", 0, 0, NULL);
+  regexes[INFORMAL_EXAMPLE_END] = g_regex_new ("</div>", 0, 0, NULL);
+  regexes[CLEAN_UP] = g_regex_new ("</?[acdehlpsu].*?>|</?td.*?>|</?ta.*?>|</?tb.*?>", 0, 0, NULL);
+
+#ifdef IDE_ENABLE_TRACE
+  for (guint i = 0; i < N_REGEXES; i++)
+    {
+      if (regexes[i] == NULL)
+        g_error ("Failed to create regex %d", i);
+    }
+#endif
+}
+
+static void
+gbp_devhelp_documentation_provider_interface_init (IdeDocumentationProviderInterface *iface)
+{
+  iface->get_name = gbp_devhelp_documentation_provider_get_name;
+  iface->get_info = gbp_devhelp_documentation_provider_get_info;
+  iface->get_context = gbp_devhelp_documentation_provider_get_context;
+}
+
+static void
+gbp_devhelp_documentation_provider_init (GbpDevhelpDocumentationProvider *self)
+{
+}
diff --git a/plugins/devhelp/gbp-devhelp-documentation-provider.h 
b/plugins/devhelp/gbp-devhelp-documentation-provider.h
new file mode 100644
index 0000000..23dbe97
--- /dev/null
+++ b/plugins/devhelp/gbp-devhelp-documentation-provider.h
@@ -0,0 +1,32 @@
+/*gbp-devhelp-documentation-provider.h
+ *
+ * Copyright (C) 2017 Lucie Charvat <luci charvat gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GBP_DEVHELP_DOCUMENTATION_PROVIDER_H
+#define GBP_DEVHELP_DOCUMENTATION_PROVIDER_H
+
+#include <ide.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_DEVHELP_DOCUMENTATION_PROVIDER (gbp_devhelp_documentation_provider_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpDevhelpDocumentationProvider, gbp_devhelp_documentation_provider, GBP, 
DEVHELP_DOCUMENTATION_PROVIDER, IdeObject)
+
+G_END_DECLS
+
+#endif /* GBP_DEVHELP_DOCUMENTATION_PROVIDER_H */
diff --git a/plugins/devhelp/gbp-devhelp-editor-view-addin.c b/plugins/devhelp/gbp-devhelp-editor-view-addin.c
index 1702c9f..8b321c2 100644
--- a/plugins/devhelp/gbp-devhelp-editor-view-addin.c
+++ b/plugins/devhelp/gbp-devhelp-editor-view-addin.c
@@ -18,11 +18,13 @@
 
 #define G_LOG_DOMAIN "gbp-devhelp-editor-view-addin"
 
+#include <libxml/xmlreader.h>
+
 #include "gbp-devhelp-editor-view-addin.h"
 
 struct _GbpDevhelpEditorViewAddin
 {
-  GObject parent_instance;
+  GObject         parent_instance;
 };
 
 static void
@@ -50,6 +52,7 @@ gbp_devhelp_editor_view_addin_load (IdeEditorViewAddin *addin,
                            G_CALLBACK (documentation_requested_cb),
                            addin,
                            G_CONNECT_SWAPPED);
+
 }
 
 static void
diff --git a/plugins/devhelp/gbp-devhelp-plugin.c b/plugins/devhelp/gbp-devhelp-plugin.c
index c0e9f67..627196c 100644
--- a/plugins/devhelp/gbp-devhelp-plugin.c
+++ b/plugins/devhelp/gbp-devhelp-plugin.c
@@ -21,6 +21,7 @@
 
 #include "gbp-devhelp-editor-addin.h"
 #include "gbp-devhelp-editor-view-addin.h"
+#include "gbp-devhelp-documentation-provider.h"
 #include "gbp-devhelp-layout-stack-addin.h"
 
 void
@@ -35,4 +36,7 @@ peas_register_types (PeasObjectModule *module)
   peas_object_module_register_extension_type (module,
                                               IDE_TYPE_LAYOUT_STACK_ADDIN,
                                               GBP_TYPE_DEVHELP_LAYOUT_STACK_ADDIN);
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_DOCUMENTATION_PROVIDER,
+                                              GBP_TYPE_DEVHELP_DOCUMENTATION_PROVIDER);
 }
diff --git a/plugins/devhelp/meson.build b/plugins/devhelp/meson.build
index 98bcd08..0c930f6 100644
--- a/plugins/devhelp/meson.build
+++ b/plugins/devhelp/meson.build
@@ -7,6 +7,8 @@ devhelp_resources = gnome.compile_resources(
 )
 
 devhelp_sources = [
+  'gbp-devhelp-documentation-provider.c',
+  'gbp-devhelp-documentation-provider.h',
   'gbp-devhelp-menu-button.c',
   'gbp-devhelp-menu-button.h',
   'gbp-devhelp-layout-stack-addin.c',
diff --git a/plugins/documentation-card/documentation-card.plugin 
b/plugins/documentation-card/documentation-card.plugin
new file mode 100644
index 0000000..b2ee576
--- /dev/null
+++ b/plugins/documentation-card/documentation-card.plugin
@@ -0,0 +1,9 @@
+[Plugin]
+Module=documentation-card-plugin
+Name=Documentation Card
+Description=Shows documentation snippets on hover.
+Authors=Lucie Charvat <luci charvat gmail com>
+Copyright=Copyright © 2017 Lucie Charvat
+Builtin=true
+Hidden=true
+Depends=editor
diff --git a/plugins/documentation-card/gbp-documentation-card-plugin.c 
b/plugins/documentation-card/gbp-documentation-card-plugin.c
new file mode 100644
index 0000000..1764925
--- /dev/null
+++ b/plugins/documentation-card/gbp-documentation-card-plugin.c
@@ -0,0 +1,30 @@
+/* gbp-documentation-card-plugin.c
+ *
+ * Copyright (C) 2017 Lucie Charvat <luci charvat gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <ide.h>
+#include <libpeas/peas.h>
+
+#include "gbp-documentation-card-view-addin.h"
+
+void
+peas_register_types (PeasObjectModule *module)
+{
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_EDITOR_VIEW_ADDIN,
+                                              GBP_TYPE_DOCUMENTATION_CARD_VIEW_ADDIN);
+}
diff --git a/plugins/documentation-card/gbp-documentation-card-resources.gresource.xml 
b/plugins/documentation-card/gbp-documentation-card-resources.gresource.xml
new file mode 100644
index 0000000..dc918e9
--- /dev/null
+++ b/plugins/documentation-card/gbp-documentation-card-resources.gresource.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gnome/builder/plugins/documentation-card">
+    <file>gbp-documentation-card.ui</file>
+  </gresource>
+</gresources>
diff --git a/plugins/documentation-card/gbp-documentation-card-view-addin.c 
b/plugins/documentation-card/gbp-documentation-card-view-addin.c
new file mode 100644
index 0000000..865855c
--- /dev/null
+++ b/plugins/documentation-card/gbp-documentation-card-view-addin.c
@@ -0,0 +1,244 @@
+/* gbp-documentation-card-view-addin.c
+ *
+ * Copyright (C) 2017 Lucie Charvat <luci charvat gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "gbp-documentation-card-view-addin"
+
+#include <ide.h>
+
+#include "gbp-documentation-card-view-addin.h"
+#include "gbp-documentation-card.h"
+
+#define POPUP_TIMEOUT          1
+#define POPDOWN_TIMEOUT        500
+#define SPACE_TOLERANCE        15
+
+struct _GbpDocumentationCardViewAddin
+{
+  GObject               parent_instance;
+
+  IdeEditorView        *editor_view;
+  GbpDocumentationCard *popover;
+  gchar                *previous_text;
+
+  guint                 timeout_id;
+  gulong                motion_handler_id;
+  guint                 poped_up : 1;
+  guint                 last_x, last_y;
+};
+
+static void iface_init (IdeEditorViewAddinInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GbpDocumentationCardViewAddin, gbp_documentation_card_view_addin, G_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (IDE_TYPE_EDITOR_VIEW_ADDIN, iface_init))
+
+static gboolean
+within_space (GbpDocumentationCardViewAddin *self,
+              guint                          x,
+              guint                          y)
+{
+  return (x <= self->last_x + SPACE_TOLERANCE &&
+      x >= self->last_x - SPACE_TOLERANCE &&
+      y <= self->last_y + SPACE_TOLERANCE &&
+      y >= self->last_y - SPACE_TOLERANCE);
+}
+
+static gboolean
+unichar_issymbol (gunichar ch)
+{
+  return g_unichar_islower (ch) || g_unichar_isdigit (ch) || ch == '_';
+}
+
+
+static gboolean
+search_document_cb (gpointer data)
+{
+  GbpDocumentationCardViewAddin *self = GBP_DOCUMENTATION_CARD_VIEW_ADDIN  (data);
+  IdeBuffer *buffer;
+  IdeSourceView *source_view;
+  GtkSourceLanguage *lang;
+  IdeContext *context;
+  IdeDocumentation *doc;
+  IdeDocumentationContext doc_context;
+
+  GdkDisplay *display;
+  GdkWindow *window;
+  GdkDevice *device;
+
+  GtkTextIter begin;
+  GtkTextIter end;
+
+  g_autoptr(IdeDocumentationInfo) info = NULL;
+  g_autofree gchar *selected_text = NULL;
+  gint x, y;
+
+  self->timeout_id = 0;
+
+  window = gtk_widget_get_parent_window (GTK_WIDGET (self->editor_view));
+  display = gdk_window_get_display (window);
+  device = gdk_seat_get_pointer (gdk_display_get_default_seat (display));
+
+  gdk_window_get_device_position (window, device, &x, &y, NULL);
+
+  if (self->poped_up)
+    {
+      if (within_space (self, x, y))
+        return G_SOURCE_REMOVE;
+      self->poped_up = FALSE;
+      gbp_documentation_card_popdown (self->popover);
+      return G_SOURCE_REMOVE;
+    }
+
+  self->last_x = x;
+  self->last_y = y;
+
+  source_view = ide_editor_view_get_view (self->editor_view);
+  if (!GTK_SOURCE_IS_VIEW (source_view))
+    return G_SOURCE_REMOVE;
+
+  buffer = ide_editor_view_get_buffer (self->editor_view);
+  if (buffer == NULL)
+    return G_SOURCE_REMOVE;
+
+  context = ide_buffer_get_context (buffer);
+  doc =  ide_context_get_documentation (context);
+
+  lang = gtk_source_buffer_get_language (GTK_SOURCE_BUFFER (buffer));
+  if (lang == NULL)
+    return G_SOURCE_REMOVE;
+
+  if (ide_str_equal0 (gtk_source_language_get_id(lang), "c"))
+    doc_context = IDE_DOCUMENTATION_CONTEXT_CARD_C;
+  else
+    return G_SOURCE_REMOVE;
+
+  gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (source_view), GTK_TEXT_WINDOW_WIDGET, x, y, &x, &y);
+  gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (source_view), &end, x, y);
+  gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (source_view), &begin, x, y);
+
+  while (unichar_issymbol (gtk_text_iter_get_char (&begin)))
+    if (!gtk_text_iter_backward_char (&begin))
+      break;
+  gtk_text_iter_forward_char (&begin);
+
+  while (unichar_issymbol (gtk_text_iter_get_char (&end)))
+    if (!gtk_text_iter_forward_char (&end))
+      break;
+
+  selected_text = gtk_text_iter_get_slice (&begin, &end);
+
+  if (g_strcmp0 (selected_text, self->previous_text) != 0)
+    {
+      info = ide_documentation_get_info (doc, selected_text, doc_context);
+      if (ide_documentation_info_get_size (info) == 0)
+        return G_SOURCE_REMOVE;
+
+      gbp_documentation_card_set_info (self->popover, info);
+      g_free (self->previous_text);
+      self->previous_text = g_steal_pointer (&selected_text);
+    }
+
+  gbp_documentation_card_popup (self->popover);
+  self->poped_up = TRUE;
+
+  return G_SOURCE_REMOVE;
+}
+
+
+
+static gboolean
+motion_notify_event_cb (gpointer data)
+{
+  GbpDocumentationCardViewAddin *self = GBP_DOCUMENTATION_CARD_VIEW_ADDIN  (data);
+
+  ide_clear_source (&self->timeout_id);
+
+  if (!self->poped_up)
+    self->timeout_id =
+            gdk_threads_add_timeout_seconds_full (G_PRIORITY_LOW,
+                                                  POPUP_TIMEOUT,
+                                                  search_document_cb,
+                                                  g_object_ref (self),
+                                                  g_object_unref);
+  else
+    search_document_cb (self);
+
+  return FALSE;
+}
+
+static void
+gbp_documentation_card_view_addin_load (IdeEditorViewAddin *addin,
+                                        IdeEditorView      *view)
+{
+  GbpDocumentationCardViewAddin *self;
+
+  g_assert (GBP_IS_DOCUMENTATION_CARD_VIEW_ADDIN (addin));
+  g_assert (IDE_IS_EDITOR_VIEW (view));
+
+  self = GBP_DOCUMENTATION_CARD_VIEW_ADDIN (addin);
+  self->editor_view = view;
+  self->popover = g_object_new (GBP_TYPE_DOCUMENTATION_CARD,
+                                "relative-to", view,
+                                "position", GTK_POS_TOP,
+                                "modal", FALSE,
+                                 NULL);
+  self->motion_handler_id =
+    g_signal_connect_object (view,
+                            "motion-notify-event",
+                            G_CALLBACK (motion_notify_event_cb),
+                            self,
+                            G_CONNECT_SWAPPED);
+
+}
+
+static void
+gbp_documentation_card_view_addin_unload (IdeEditorViewAddin *addin,
+                                          IdeEditorView      *view)
+{
+  GbpDocumentationCardViewAddin *self;
+
+  g_assert (GBP_IS_DOCUMENTATION_CARD_VIEW_ADDIN (addin));
+  g_assert (IDE_IS_EDITOR_VIEW (view));
+
+  self = GBP_DOCUMENTATION_CARD_VIEW_ADDIN (addin);
+
+  ide_clear_source (&self->timeout_id);
+  ide_clear_signal_handler (self->editor_view, &self->motion_handler_id);
+
+  g_free (self->previous_text);
+  gtk_widget_destroy (GTK_WIDGET (self->popover));
+  self->popover = NULL;
+  self->editor_view = NULL;
+
+}
+
+static void
+gbp_documentation_card_view_addin_class_init (GbpDocumentationCardViewAddinClass *klass)
+{
+}
+
+static void
+gbp_documentation_card_view_addin_init (GbpDocumentationCardViewAddin *self)
+{
+}
+
+static void
+iface_init (IdeEditorViewAddinInterface *iface)
+{
+  iface->load = gbp_documentation_card_view_addin_load;
+  iface->unload = gbp_documentation_card_view_addin_unload;
+}
diff --git a/plugins/documentation-card/gbp-documentation-card-view-addin.h 
b/plugins/documentation-card/gbp-documentation-card-view-addin.h
new file mode 100644
index 0000000..1ee86c2
--- /dev/null
+++ b/plugins/documentation-card/gbp-documentation-card-view-addin.h
@@ -0,0 +1,30 @@
+/* gbp-documentation-card-view-addin.h
+ *
+ * Copyright (C) 2017 Lucie Charvat <luci charvat gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GBP_DOCUMENTATION_CARD_VIEW_ADDIN_H
+#define GBP_DOCUMENTATION_CARD_VIEW_ADDIN_H
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_DOCUMENTATION_CARD_VIEW_ADDIN (gbp_documentation_card_view_addin_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpDocumentationCardViewAddin, gbp_documentation_card_view_addin, GBP, 
DOCUMENTATION_CARD_VIEW_ADDIN, GObject)
+
+G_END_DECLS
+
+#endif /* GBP_DOCUMENTATION_CARD_VIEW_ADDIN_H */
diff --git a/plugins/documentation-card/gbp-documentation-card.c 
b/plugins/documentation-card/gbp-documentation-card.c
new file mode 100644
index 0000000..231bbc4
--- /dev/null
+++ b/plugins/documentation-card/gbp-documentation-card.c
@@ -0,0 +1,146 @@
+/* gbp-documentation-card.c
+ *
+ * Copyright (C) 2017 Lucie Charvat <luci charvat gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "gbp-documentation-card"
+
+#include <glib/gi18n.h>
+#include <ide.h>
+
+#include "gbp-documentation-card.h"
+
+#define CARD_WIDTH             80
+
+struct _GbpDocumentationCard
+{
+  GtkPopover    parent_instance;
+
+  IdeEditorView  *view;
+  GdkWindow      *window;
+  GdkDevice      *pointer;
+
+  GtkButton    *button;
+  GtkEntry     *entry;
+  GtkLabel     *header;
+  GtkLabel     *text;
+
+  gchar        *uri;
+};
+
+G_DEFINE_TYPE (GbpDocumentationCard, gbp_documentation_card, GTK_TYPE_POPOVER)
+
+static gboolean
+card_popup (GbpDocumentationCard *self)
+{
+  GdkRectangle rec = {1, 1, 1, 1};
+
+  gdk_window_get_device_position (self->window, self->pointer, &rec.x, &rec.y, NULL);
+  gtk_popover_set_pointing_to (GTK_POPOVER (self), &rec);
+  gtk_popover_popup (GTK_POPOVER (self));
+
+  return FALSE;
+}
+
+static gboolean
+card_popdown (GbpDocumentationCard *self)
+{
+  gtk_popover_popdown (GTK_POPOVER (self));
+  gtk_popover_set_modal (GTK_POPOVER (self), FALSE);
+
+  gtk_widget_set_visible (GTK_WIDGET (self->text), FALSE);
+  gtk_widget_set_visible (GTK_WIDGET (self->button), TRUE);
+
+  return FALSE;
+}
+
+static void
+gbp_documentation_card__button_clicked (GbpDocumentationCard *self,
+                                        GtkButton            *button)
+{
+  g_assert (GBP_IS_DOCUMENTATION_CARD (self));
+  g_assert (GTK_IS_BUTTON (button));
+
+  gtk_widget_set_visible (GTK_WIDGET (self->text), TRUE);
+  gtk_widget_set_visible (GTK_WIDGET (self->button), FALSE);
+
+  gtk_popover_set_modal (GTK_POPOVER (self), TRUE);
+  gtk_label_set_width_chars (self->text, CARD_WIDTH);
+
+}
+
+static void
+gbp_documentation_card_class_init (GbpDocumentationCardClass *klass)
+{
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/builder/plugins/documentation-card/gbp-documentation-card.ui");
+  gtk_widget_class_bind_template_child (widget_class, GbpDocumentationCard, button);
+  gtk_widget_class_bind_template_child (widget_class, GbpDocumentationCard, header);
+  gtk_widget_class_bind_template_child (widget_class, GbpDocumentationCard, text);
+
+}
+
+static void
+gbp_documentation_card_init (GbpDocumentationCard *self)
+{
+
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  g_signal_connect_object (self->button,
+                           "clicked",
+                           G_CALLBACK (gbp_documentation_card__button_clicked),
+                           self,
+                           G_CONNECT_SWAPPED);
+}
+
+void
+gbp_documentation_card_set_info (GbpDocumentationCard *self,
+                                 IdeDocumentationInfo *info)
+{
+  IdeDocumentationProposal *proposal;
+
+  g_assert (GBP_IS_DOCUMENTATION_CARD (self));
+  g_assert (IDE_IS_DOCUMENTATION_INFO (info));
+
+  proposal = ide_documentation_info_get_proposal (info, 0);
+
+  gtk_label_set_markup (self->text, ide_documentation_proposal_get_text (proposal));
+  gtk_label_set_markup (self->header, ide_documentation_proposal_get_header (proposal));
+}
+
+void
+gbp_documentation_card_popup (GbpDocumentationCard *self)
+{
+  GdkDisplay *display;
+
+  g_return_if_fail (GBP_IS_DOCUMENTATION_CARD (self));
+
+  self->window = gtk_widget_get_parent_window (gtk_popover_get_relative_to (GTK_POPOVER (self)));
+  display = gdk_window_get_display (self->window);
+  self->pointer = gdk_seat_get_pointer (gdk_display_get_default_seat (display));
+
+  card_popup (self);
+}
+
+void
+gbp_documentation_card_popdown (GbpDocumentationCard *self)
+{
+  g_return_if_fail (GBP_IS_DOCUMENTATION_CARD (self));
+
+  card_popdown (self);
+}
+
diff --git a/plugins/documentation-card/gbp-documentation-card.h 
b/plugins/documentation-card/gbp-documentation-card.h
new file mode 100644
index 0000000..7b462a5
--- /dev/null
+++ b/plugins/documentation-card/gbp-documentation-card.h
@@ -0,0 +1,36 @@
+/* gbp-documentation-card.h
+ *
+ * Copyright (C) 2017 Lucie Charvat <luci charvat gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License//
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GBP_DOCUMENTATION_CARD_H
+#define GBP_DOCUMENTATION_CARD_H
+
+#include <ide.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_DOCUMENTATION_CARD (gbp_documentation_card_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpDocumentationCard, gbp_documentation_card, GBP, DOCUMENTATION_CARD, GtkPopover)
+
+void gbp_documentation_card_set_info (GbpDocumentationCard *self,
+                                      IdeDocumentationInfo *info);
+void gbp_documentation_card_popup    (GbpDocumentationCard *self);
+void gbp_documentation_card_popdown  (GbpDocumentationCard *self);
+G_END_DECLS
+
+#endif /* GB_DOCUMENTATION_CARD_H */
diff --git a/plugins/documentation-card/gbp-documentation-card.ui 
b/plugins/documentation-card/gbp-documentation-card.ui
new file mode 100644
index 0000000..d6a0d19
--- /dev/null
+++ b/plugins/documentation-card/gbp-documentation-card.ui
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.16 -->
+  <template class="GbpDocumentationCard" parent="GtkPopover">
+    <child>
+      <object class="GtkScrolledWindow">
+        <property name="visible">True</property>
+        <property name="hscrollbar-policy">never</property>
+        <property name="vexpand">True</property>
+        <property name="propagate-natural-height">true</property>
+        <property name="max-content-height">600</property>
+          <child>
+            <object class="GtkBox">
+              <property name="border-width">12</property>
+              <property name="orientation">vertical</property>
+              <property name="spacing">6</property>
+              <property name="visible">true</property>
+                <child>
+                  <object class="GtkLabel" id="header">
+                    <property name="xalign">0.0</property>
+                    <property name="visible">true</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="text">
+                    <property name="xalign">0.0</property>
+                    <property name="visible">false</property>
+                    <property name="wrap">1</property>
+                    <style>
+                      <class name="dim-label"/>
+                    </style>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkButton" id="button">
+                    <property name="label" translatable="yes">_Show more</property>
+                    <property name="use-underline">true</property>
+                    <property name="visible">true</property>
+                    <style>
+                      <class name="flat"/>
+                    </style>
+                  </object>
+                </child>
+            </object>
+          </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/plugins/documentation-card/meson.build b/plugins/documentation-card/meson.build
new file mode 100644
index 0000000..8e9b3ff
--- /dev/null
+++ b/plugins/documentation-card/meson.build
@@ -0,0 +1,28 @@
+if get_option('with_documentation_card')
+
+documentation_card_resources = gnome.compile_resources(
+  'gbp-documentation-card-resources',
+  'gbp-documentation-card-resources.gresource.xml',
+  c_name: 'gbp_documentation_card',
+)
+
+documentation_card_sources = [
+  'gbp-documentation-card.c',
+  'gbp-documentation-card.h',
+  'gbp-documentation-card-view-addin.c',
+  'gbp-documentation-card-view-addin.h',
+  'gbp-documentation-card-plugin.c',
+  documentation_card_resources[0],
+]
+
+shared_module('documentation-card-plugin', documentation_card_sources,
+  dependencies: plugin_deps,
+  link_args: plugin_link_args,
+  link_depends: plugin_link_deps,
+  install: true,
+  install_dir: plugindir,
+)
+
+install_data('documentation-card.plugin', install_dir: plugindir)
+
+endif
diff --git a/plugins/meson.build b/plugins/meson.build
index dbfcd84..c16c521 100644
--- a/plugins/meson.build
+++ b/plugins/meson.build
@@ -22,6 +22,7 @@ subdir('command-bar')
 subdir('comment-code')
 subdir('create-project')
 subdir('ctags')
+subdir('documentation-card')
 subdir('devhelp')
 subdir('eslint')
 subdir('file-search')


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