[gnome-builder] plugins: add a comment code plugin



commit 0c50486e62a4663c10472b4e61f96b24c38c96fb
Author: Sebastien Lafargue <slafargue gnome org>
Date:   Sat Feb 27 10:54:19 2016 +0100

    plugins: add a comment code plugin

 configure.ac                                       |    2 +
 libide/editor/ide-editor-frame.c                   |   15 +
 libide/editor/ide-editor-frame.h                   |    8 +-
 libide/editor/ide-editor-view.c                    |   17 +
 libide/editor/ide-editor-view.h                    |    4 +-
 libide/ide-source-view-movements.c                 |    8 +-
 libide/ide-text-iter.c                             |   55 ++-
 libide/ide-text-iter.h                             |    2 +
 plugins/Makefile.am                                |    1 +
 plugins/comment-code/Makefile.am                   |   45 +++
 plugins/comment-code/comment-code.plugin           |   10 +
 plugins/comment-code/configure.ac                  |   12 +
 plugins/comment-code/gbp-comment-code-plugin.c     |   30 ++
 plugins/comment-code/gbp-comment-code-view-addin.c |  415 ++++++++++++++++++++
 plugins/comment-code/gbp-comment-code-view-addin.h |   30 ++
 .../comment-code/gbp-comment-code.gresource.xml    |    6 +
 plugins/comment-code/gtk/menus.ui                  |   22 +
 17 files changed, 658 insertions(+), 24 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index dbf1b21..1df0716 100644
--- a/configure.ac
+++ b/configure.ac
@@ -237,6 +237,7 @@ m4_include([plugins/build-tools/configure.ac])
 m4_include([plugins/c-pack/configure.ac])
 m4_include([plugins/clang/configure.ac])
 m4_include([plugins/command-bar/configure.ac])
+m4_include([plugins/comment-code/configure.ac])
 m4_include([plugins/contributing/configure.ac])
 m4_include([plugins/create-project/configure.ac])
 m4_include([plugins/ctags/configure.ac])
@@ -482,6 +483,7 @@ echo "  Build Tools .......................... : ${enable_build_tools_plugin}"
 echo "  C Language Pack ...................... : ${enable_c_pack_plugin}"
 echo "  Clang ................................ : ${enable_clang_plugin}"
 echo "  Command Bar .......................... : ${enable_command_bar_plugin}"
+echo "  Comment Code.......................... : ${enable_comment_code_plugin}"
 echo "  Contribute ........................... : ${enable_contributing_plugin}"
 echo "  Ctags ................................ : ${enable_ctags_plugin}"
 echo "  Devhelp .............................. : ${enable_devhelp_plugin}"
diff --git a/libide/editor/ide-editor-frame.c b/libide/editor/ide-editor-frame.c
index e2ead8a..0f2b137 100644
--- a/libide/editor/ide-editor-frame.c
+++ b/libide/editor/ide-editor-frame.c
@@ -270,6 +270,21 @@ on_cursor_moved (IdeBuffer         *buffer,
 }
 
 /**
+ * ide_editor_frame_get_source_view:
+ *
+ * Gets the #IdeEditorFrame:document property.
+ *
+ * Returns: (transfer none) (nullable): An #IdeSourceView or %NULL.
+ */
+IdeSourceView *
+ide_editor_frame_get_source_view (IdeEditorFrame *self)
+{
+  g_return_val_if_fail (IDE_IS_EDITOR_FRAME (self), NULL);
+
+  return self->source_view;
+}
+
+/**
  * ide_editor_frame_get_document:
  *
  * Gets the #IdeEditorFrame:document property.
diff --git a/libide/editor/ide-editor-frame.h b/libide/editor/ide-editor-frame.h
index 5b3c7c4..6fd862c 100644
--- a/libide/editor/ide-editor-frame.h
+++ b/libide/editor/ide-editor-frame.h
@@ -22,6 +22,7 @@
 #include <gtk/gtk.h>
 
 #include "ide-buffer.h"
+#include "ide-source-view.h"
 
 G_BEGIN_DECLS
 
@@ -29,9 +30,10 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (IdeEditorFrame, ide_editor_frame, IDE, EDITOR_FRAME, GtkBin)
 
-IdeBuffer *ide_editor_frame_get_document (IdeEditorFrame *self);
-void       ide_editor_frame_set_document (IdeEditorFrame *self,
-                                          IdeBuffer      *buffer);
+IdeBuffer       *ide_editor_frame_get_document      (IdeEditorFrame *self);
+void             ide_editor_frame_set_document      (IdeEditorFrame *self,
+                                                     IdeBuffer      *buffer);
+IdeSourceView   *ide_editor_frame_get_source_view   (IdeEditorFrame *self);
 
 G_END_DECLS
 
diff --git a/libide/editor/ide-editor-view.c b/libide/editor/ide-editor-view.c
index dd07bcb..e0937cb 100644
--- a/libide/editor/ide-editor-view.c
+++ b/libide/editor/ide-editor-view.c
@@ -990,6 +990,23 @@ ide_editor_view_init (IdeEditorView *self)
 }
 
 /**
+ * ide_editor_view_get_active_source_view:
+ *
+ * Returns: (transfer none): An #IdeSourceView.
+ */
+IdeSourceView *
+ide_editor_view_get_active_source_view (IdeEditorView *self)
+{
+  IdeEditorFrame *frame;
+
+  g_return_val_if_fail (IDE_IS_EDITOR_VIEW (self), NULL);
+
+  frame = ide_editor_view_get_last_focused (self);
+
+  return ide_editor_frame_get_source_view (frame);
+}
+
+/**
  * ide_editor_view_get_document:
  *
  * Returns: (transfer none): An #IdeBuffer.
diff --git a/libide/editor/ide-editor-view.h b/libide/editor/ide-editor-view.h
index 353390b..f256559 100644
--- a/libide/editor/ide-editor-view.h
+++ b/libide/editor/ide-editor-view.h
@@ -20,6 +20,7 @@
 #define IDE_EDITOR_VIEW_H
 
 #include "ide-buffer.h"
+#include "ide-source-view.h"
 #include "ide-layout-view.h"
 
 G_BEGIN_DECLS
@@ -28,7 +29,8 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (IdeEditorView, ide_editor_view, IDE, EDITOR_VIEW, IdeLayoutView)
 
-IdeBuffer *ide_editor_view_get_document (IdeEditorView *self);
+IdeBuffer      *ide_editor_view_get_document              (IdeEditorView *self);
+IdeSourceView  *ide_editor_view_get_active_source_view    (IdeEditorView *self);
 
 G_END_DECLS
 
diff --git a/libide/ide-source-view-movements.c b/libide/ide-source-view-movements.c
index 5e1f82c..48cf6ca 100644
--- a/libide/ide-source-view-movements.c
+++ b/libide/ide-source-view-movements.c
@@ -1241,7 +1241,7 @@ match_comments (GtkTextIter *insert,
 
   if (comment_start && !gtk_text_iter_is_end (&cursor))
     {
-      if (_ide_text_iter_find_chars_forward (&cursor, NULL, "*/", FALSE))
+      if (_ide_text_iter_find_chars_forward (&cursor, NULL, NULL, "*/", FALSE))
         {
           gtk_text_iter_forward_char (&cursor);
           *insert = cursor;
@@ -1251,7 +1251,7 @@ match_comments (GtkTextIter *insert,
     }
   else if (!comment_start && !gtk_text_iter_is_start (&cursor))
     {
-      if (_ide_text_iter_find_chars_backward (&cursor, NULL, "/*", FALSE))
+      if (_ide_text_iter_find_chars_backward (&cursor, NULL, NULL, "/*", FALSE))
         {
           *insert = cursor;
 
@@ -2417,11 +2417,11 @@ find_html_tag (GtkTextIter      *iter,
 
       return tag;
     }
-  else if (_ide_text_iter_find_chars_forward (&cursor, &end, "!--", TRUE))
+  else if (_ide_text_iter_find_chars_forward (&cursor, NULL, &end, "!--", TRUE))
     {
       tag->kind = HTML_TAG_KIND_COMMENT;
       cursor = end;
-      if (_ide_text_iter_find_chars_forward (&cursor, &end, "-->", FALSE))
+      if (_ide_text_iter_find_chars_forward (&cursor, NULL, &end, "-->", FALSE))
         {
           tag->end = end;
           if (direction == GTK_DIR_RIGHT)
diff --git a/libide/ide-text-iter.c b/libide/ide-text-iter.c
index d463581..4c0ca68 100644
--- a/libide/ide-text-iter.c
+++ b/libide/ide-text-iter.c
@@ -623,11 +623,12 @@ _ide_text_iter_in_string (GtkTextIter *iter,
 /**
  * _ide_text_iter_find_chars_backward:
  * @iter: A #GtkTextIter indicating the start position to check for.
- * end: (out): A #GtkTextIter returning the str end iter (if found).
+ * @limit: (nullable): A #GtkTextIter indicating the limit of the search.
+ * @end: (out) (nullable): A #GtkTextIter returning the str end iter (if found).
  * @str: A C type string.
  * @only_at_start: %TRUE if the searched @str string should be constrained to start @iter position.
  *
- * Search backward for a @str string, starting at @iter position.
+ * Search backward for a @str string, starting at @iter position till @limit if there's one.
  * In case of succes, @iter is updated to @str start position.
  *
  * Notice that for @str to be found, @iter need to be at least on the @str last char
@@ -636,12 +637,13 @@ _ide_text_iter_in_string (GtkTextIter *iter,
  */
 gboolean
 _ide_text_iter_find_chars_backward (GtkTextIter *iter,
+                                    GtkTextIter *limit,
                                     GtkTextIter *end,
                                     const gchar *str,
                                     gboolean     only_at_start)
 {
   const gchar *base_str;
-  const gchar *limit;
+  const gchar *str_limit;
   GtkTextIter base_cursor;
 
   g_return_val_if_fail (!ide_str_empty0 (str), FALSE);
@@ -649,7 +651,7 @@ _ide_text_iter_find_chars_backward (GtkTextIter *iter,
   if (!gtk_text_iter_backward_char (iter))
     return FALSE;
 
-  limit = str;
+  str_limit = str;
   base_str = str = str + strlen (str) - 1;
   base_cursor = *iter;
   do
@@ -665,7 +667,7 @@ _ide_text_iter_find_chars_backward (GtkTextIter *iter,
                 break;
             }
 
-          str = g_utf8_find_prev_char (limit, str);
+          str = g_utf8_find_prev_char (str_limit, str);
           if (str == NULL)
             {
               if (end)
@@ -692,28 +694,51 @@ _ide_text_iter_find_chars_backward (GtkTextIter *iter,
 /**
  * _ide_text_iter_find_chars_forward:
  * @iter: A #GtkTextIter indicating the start position to check for.
- * end: (out): A #GtkTextIter returning the str end iter (if found).
+ * @limit: (nullable): A #GtkTextIter indicating the limit of the search.
+ * @end: (out) (nullable): A #GtkTextIter returning the str end iter (if found).
  * @str: A C type string.
  * @only_at_start: %TRUE if the searched @str string should be constrained to start @iter position.
  *
- * Search forward for a @str string, starting at @iter position.
- * In case of succes, @iter is updated to @str start position.
+ * Search forward for a @str string, starting at @iter position till @limit if there's one.
+ * In case of succes, @iter is updated to the found @str start position,
+ * otherwise, its position is undefined.
  *
  * Returns: %TRUE if case of succes, %FALSE otherwise.
  */
 gboolean
 _ide_text_iter_find_chars_forward (GtkTextIter *iter,
+                                   GtkTextIter *limit,
                                    GtkTextIter *end,
                                    const gchar *str,
                                    gboolean     only_at_start)
 {
   const gchar *base_str;
-  const gchar *limit;
+  const gchar *str_limit;
   GtkTextIter base_cursor;
+  GtkTextIter real_limit;
+  gint str_char_len;
+  gint real_limit_offset;
 
   g_return_val_if_fail (!ide_str_empty0 (str), FALSE);
 
-  limit = str + strlen (str);
+  if (limit == NULL)
+    {
+      real_limit = *iter;
+      gtk_text_iter_forward_to_end (&real_limit);
+    }
+  else
+    real_limit = *limit;
+
+  str_char_len = g_utf8_strlen (str, -1);
+  real_limit_offset = gtk_text_iter_get_offset (&real_limit) - str_char_len;
+  if (real_limit_offset < 0)
+    return FALSE;
+
+  gtk_text_iter_set_offset (&real_limit, real_limit_offset);
+  if (gtk_text_iter_compare(iter, &real_limit) > 0)
+    return FALSE;
+
+  str_limit = str + strlen (str);
   base_str = str;
   base_cursor = *iter;
   do
@@ -729,7 +754,7 @@ _ide_text_iter_find_chars_forward (GtkTextIter *iter,
                 break;
             }
 
-          str = g_utf8_find_next_char (str, limit);
+          str = g_utf8_find_next_char (str, str_limit);
           if (str == NULL)
             {
               if (end)
@@ -744,11 +769,9 @@ _ide_text_iter_find_chars_forward (GtkTextIter *iter,
 
         } while ((gtk_text_iter_forward_char (iter)));
 
-      if (gtk_text_iter_is_end (iter))
-        return FALSE;
-      else
-        str = base_str;
-    } while (gtk_text_iter_forward_char (&base_cursor));
+    } while (gtk_text_iter_compare(&base_cursor, &real_limit) < 0 &&
+             (str = base_str) &&
+             gtk_text_iter_forward_char (&base_cursor));
 
   return FALSE;
 }
diff --git a/libide/ide-text-iter.h b/libide/ide-text-iter.h
index 0c8ff5f..0148fbb 100644
--- a/libide/ide-text-iter.h
+++ b/libide/ide-text-iter.h
@@ -51,10 +51,12 @@ gboolean _ide_text_iter_in_string                (GtkTextIter              *iter
                                                   GtkTextIter              *str_end,
                                                   gboolean                  include_str_bounds);
 gboolean _ide_text_iter_find_chars_backward      (GtkTextIter              *iter,
+                                                  GtkTextIter              *limit,
                                                   GtkTextIter              *end,
                                                   const gchar              *str,
                                                   gboolean                  only_at_start);
 gboolean _ide_text_iter_find_chars_forward       (GtkTextIter              *iter,
+                                                  GtkTextIter              *limit,
                                                   GtkTextIter              *end,
                                                   const gchar              *str,
                                                   gboolean                  only_at_start);
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 7ccd8fe..2b08472 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -5,6 +5,7 @@ SUBDIRS = \
        command-bar \
        contributing \
        c-pack \
+       comment-code \
        create-project \
        ctags \
        devhelp \
diff --git a/plugins/comment-code/Makefile.am b/plugins/comment-code/Makefile.am
new file mode 100644
index 0000000..e928ef9
--- /dev/null
+++ b/plugins/comment-code/Makefile.am
@@ -0,0 +1,45 @@
+if ENABLE_COMMENT_CODE_PLUGIN
+
+DISTCLEANFILES =
+BUILT_SOURCES =
+CLEANFILES =
+EXTRA_DIST = $(plugin_DATA)
+
+plugindir = $(libdir)/gnome-builder/plugins
+plugin_LTLIBRARIES = libcomment-code-plugin.la
+dist_plugin_DATA = comment-code.plugin
+
+libcomment_code_plugin_la_SOURCES = \
+       gbp-comment-code-plugin.c \
+       gbp-comment-code-view-addin.c \
+       gbp-comment-code-view-addin.h \
+       $(NULL)
+
+nodist_libcomment_code_plugin_la_SOURCES = \
+       gbp-comment-code-resources.c \
+       gbp-comment-code-resources.h
+
+libcomment_code_plugin_la_CFLAGS = \
+       $(LIBIDE_CFLAGS) \
+       $(OPTIMIZE_CFLAGS) \
+       -I$(top_srcdir)/libide \
+       $(NULL)
+
+libcomment_code_plugin_la_LDFLAGS = \
+       $(OPTIMIZE_LDFLAGS) \
+       -avoid-version \
+       -module \
+       -export-regex peas_register_types \
+       $(NULL)
+
+glib_resources_c = gbp-comment-code-resources.c
+glib_resources_h = gbp-comment-code-resources.h
+glib_resources_xml = gbp-comment-code.gresource.xml
+glib_resources_namespace = gbp_comment_code
+include $(top_srcdir)/build/autotools/Makefile.am.gresources
+
+include $(top_srcdir)/plugins/Makefile.plugin
+
+endif
+
+-include $(top_srcdir)/git.mk
diff --git a/plugins/comment-code/comment-code.plugin b/plugins/comment-code/comment-code.plugin
new file mode 100644
index 0000000..d4bc93f
--- /dev/null
+++ b/plugins/comment-code/comment-code.plugin
@@ -0,0 +1,10 @@
+[Plugin]
+Module=comment-code-plugin
+Name=Comment Code
+Description=Comment code lines with Builder editor.
+Authors=Sebastien Lafargue <slafargue gnome org>
+Copyright=Copyright © 2016 Sebastien Lafargue
+Builtin=true
+Hidden=true
+X-Tool-Name=comment-code
+X-Tool-Description=Comment code lines
diff --git a/plugins/comment-code/configure.ac b/plugins/comment-code/configure.ac
new file mode 100644
index 0000000..261cb1e
--- /dev/null
+++ b/plugins/comment-code/configure.ac
@@ -0,0 +1,12 @@
+# --enable-comment-code-plugin=yes/no
+AC_ARG_ENABLE([comment-code-plugin],
+              [AS_HELP_STRING([--enable-comment-code-plugin=@<:@yes/no@:>@],
+                              [Build with support for commenting code lines.])],
+              [enable_comment_code_plugin=$enableval],
+              [enable_comment_code_plugin=yes])
+
+# for if ENABLE_COMMENT_CODE_PLUGIN in Makefile.am
+AM_CONDITIONAL(ENABLE_COMMENT_CODE_PLUGIN, test x$enable_comment_code_plugin != xno)
+
+# Ensure our makefile is generated by autoconf
+AC_CONFIG_FILES([plugins/comment-code/Makefile])
diff --git a/plugins/comment-code/gbp-comment-code-plugin.c b/plugins/comment-code/gbp-comment-code-plugin.c
new file mode 100644
index 0000000..5137a71
--- /dev/null
+++ b/plugins/comment-code/gbp-comment-code-plugin.c
@@ -0,0 +1,30 @@
+/* gbp-comment-code-plugin.c
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome 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/>.
+ */
+
+#include <ide.h>
+#include <libpeas/peas.h>
+
+#include "gbp-comment-code-view-addin.h"
+
+void
+peas_register_types (PeasObjectModule *module)
+{
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_EDITOR_VIEW_ADDIN,
+                                              GBP_TYPE_COMMENT_CODE_VIEW_ADDIN);
+}
diff --git a/plugins/comment-code/gbp-comment-code-view-addin.c 
b/plugins/comment-code/gbp-comment-code-view-addin.c
new file mode 100644
index 0000000..aae79bf
--- /dev/null
+++ b/plugins/comment-code/gbp-comment-code-view-addin.c
@@ -0,0 +1,415 @@
+/* gbp-comment-code-view-addin.c
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome 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/>.
+ */
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtksourceview/gtksource.h>
+
+#include <ide.h>
+#include "ide-text-iter.h"
+
+#include "gbp-comment-code-view-addin.h"
+
+struct _GbpCommentCodeViewAddin
+{
+  GObject        parent_instance;
+
+  IdeEditorView *editor_view;
+};
+
+static void iface_init (IdeEditorViewAddinInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GbpCommentCodeViewAddin, gbp_comment_code_view_addin, G_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (IDE_TYPE_EDITOR_VIEW_ADDIN, iface_init))
+
+/* If there's only empty lines, G_MAXINT is returned */
+static gint
+get_buffer_range_min_indent (GtkTextBuffer *buffer,
+                             gint           start_line,
+                             gint           end_line)
+{
+  GtkTextIter iter;
+  gint current_indent;
+  gint min_indent = G_MAXINT;
+
+  for (gint line = start_line; line <= end_line; ++line)
+    {
+      current_indent = 0;
+      gtk_text_buffer_get_iter_at_line (buffer, &iter, line);
+      while (!gtk_text_iter_ends_line (&iter) && g_unichar_isspace (gtk_text_iter_get_char (&iter)))
+        {
+          gtk_text_iter_forward_char (&iter);
+          ++current_indent;
+        }
+
+      if (gtk_text_iter_ends_line (&iter))
+        continue;
+      else
+        min_indent = MIN (min_indent, current_indent);
+    }
+
+  return min_indent;
+}
+
+/* Empty lines, with only spaces and tabs or already commented from the start
+ * are returned as not commentables.
+ */
+static gboolean
+is_line_commentable (GtkTextBuffer *buffer,
+                     gint           line,
+                     const gchar   *start_tag)
+{
+  GtkTextIter iter;
+
+  gtk_text_buffer_get_iter_at_line (buffer, &iter, line);
+  if (gtk_text_iter_is_end (&iter))
+    return FALSE;
+
+  while (g_unichar_isspace (gtk_text_iter_get_char (&iter)))
+    {
+      if (gtk_text_iter_ends_line (&iter) ||
+          !gtk_text_iter_forward_char (&iter))
+        return FALSE;
+    }
+
+  if (_ide_text_iter_find_chars_forward (&iter, NULL, NULL, start_tag, TRUE))
+    return FALSE;
+
+  return TRUE;
+}
+
+/* Empty lines, with only spaces and tabs or not commented from the start
+ * are returned as not uncommentables.
+ * If TRUE, the start_tag_begin and start_tag_end are updated respectively
+ * to the start_tag begin and end positions.
+ */
+static gboolean
+is_line_uncommentable (GtkTextBuffer *buffer,
+                       gint           line,
+                       const gchar   *start_tag,
+                       GtkTextIter   *start_tag_begin,
+                       GtkTextIter   *start_tag_end)
+{
+  GtkTextIter iter;
+
+  gtk_text_buffer_get_iter_at_line (buffer, &iter, line);
+  if (gtk_text_iter_is_end (&iter))
+    return FALSE;
+
+  while (g_unichar_isspace (gtk_text_iter_get_char (&iter)))
+    {
+      if (gtk_text_iter_ends_line (&iter) ||
+          !gtk_text_iter_forward_char (&iter))
+        return FALSE;
+    }
+
+  if (_ide_text_iter_find_chars_forward (&iter, NULL, start_tag_end, start_tag, TRUE))
+    {
+      *start_tag_begin = iter;
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+/* start_offset, in chars, is where we insert the start_tag.
+ * Empty lines or containing only spaces or tabs are skipped.
+ */
+static void
+gbp_comment_code_view_addin_comment_line (GtkTextBuffer *buffer,
+                                          const gchar   *start_tag,
+                                          const gchar   *end_tag,
+                                          gint           line,
+                                          gint           start_offset,
+                                          gboolean       is_block_tag)
+{
+  g_autofree gchar *start_tag_str = NULL;
+  g_autofree gchar *end_tag_str = NULL;
+  GtkTextIter start;
+  GtkTextIter previous;
+  GtkTextIter end_of_line;
+  gboolean res;
+
+  g_assert (GTK_IS_TEXT_BUFFER (buffer));
+  g_assert (!ide_str_empty0 (start_tag));
+  g_assert ((is_block_tag && !ide_str_empty0 (end_tag)) || !is_block_tag);
+  g_assert (line >= 0 && line < gtk_text_buffer_get_line_count(buffer));
+
+  if (!is_line_commentable (buffer, line, start_tag))
+    return;
+
+  gtk_text_buffer_get_iter_at_line_offset (buffer, &start, line, start_offset);
+  if (gtk_text_iter_ends_line (&start))
+    return;
+
+  start_tag_str = g_strconcat (start_tag, " ", NULL);
+  gtk_text_buffer_insert (buffer, &start, start_tag_str, -1);
+  if (!is_block_tag)
+    return;
+
+  end_of_line = start;
+  gtk_text_iter_forward_to_line_end (&end_of_line);
+
+  while ((res = _ide_text_iter_find_chars_forward (&start, &end_of_line, NULL, start_tag, FALSE)))
+    {
+      previous = start;
+      gtk_text_iter_backward_char (&previous);
+      if (gtk_text_iter_get_char (&previous) != '\\')
+        break;
+
+      gtk_text_iter_forward_char (&start);
+    }
+
+  if (!res)
+    {
+      start = end_of_line;
+      end_tag_str = g_strconcat (" ", end_tag, NULL);
+    }
+  else
+    end_tag_str = g_strconcat (" ", end_tag, " ", NULL);
+
+  gtk_text_buffer_insert (buffer, &start, end_tag_str, -1);
+}
+
+static void
+gbp_comment_code_view_addin_uncomment_line (GtkTextBuffer *buffer,
+                                            const gchar   *start_tag,
+                                            const gchar   *end_tag,
+                                            gint           line,
+                                            gboolean       is_block_tag)
+{
+  GtkTextIter end_of_line;
+  GtkTextIter tag_begin;
+  GtkTextIter tag_end;
+  GtkTextIter tmp_iter;
+  GtkTextIter previous;
+  gunichar ch;
+  gboolean res;
+
+  g_assert (GTK_IS_TEXT_BUFFER (buffer));
+  g_assert (!ide_str_empty0 (start_tag));
+  g_assert ((is_block_tag && !ide_str_empty0 (end_tag)) || !is_block_tag);
+  g_assert (line >= 0 && line < gtk_text_buffer_get_line_count(buffer));
+
+  if (!is_line_uncommentable (buffer, line, start_tag, &tag_begin, &tag_end))
+    return;
+
+  gtk_text_buffer_delete (buffer, &tag_begin, &tag_end);
+  ch = gtk_text_iter_get_char (&tag_begin);
+  if (ch == ' ' || ch == '\t')
+    {
+      gtk_text_iter_forward_char (&tag_end);
+      gtk_text_buffer_delete (buffer, &tag_begin, &tag_end);
+    }
+
+  if (!is_block_tag)
+    return;
+
+  end_of_line = tag_begin;
+  gtk_text_iter_forward_to_line_end (&end_of_line);
+  while ((res = _ide_text_iter_find_chars_forward (&tag_begin, &end_of_line, &tag_end, end_tag, FALSE)))
+    {
+      previous = tag_begin;
+      gtk_text_iter_backward_char (&previous);
+      if (gtk_text_iter_get_char (&previous) != '\\')
+        break;
+
+      gtk_text_iter_forward_char (&tag_begin);
+    }
+
+  if (res)
+    {
+      tmp_iter = tag_begin;
+      gtk_text_iter_backward_char (&tmp_iter);
+      ch = gtk_text_iter_get_char (&tmp_iter);
+      if (ch == ' ' || ch == '\t')
+        tag_begin = tmp_iter;
+
+      tmp_iter = tag_end;
+      gtk_text_iter_forward_char (&tmp_iter);
+      ch = gtk_text_iter_get_char (&tmp_iter);
+      if (ch == ' ' || ch == '\t')
+        {
+          tag_end = tmp_iter;
+          gtk_text_iter_forward_char (&tag_end);
+        }
+
+      gtk_text_buffer_delete (buffer, &tag_begin, &tag_end);
+    }
+}
+
+static void
+gbp_comment_code_view_addin_comment_action (GSimpleAction *action,
+                                            GVariant      *variant,
+                                            gpointer       user_data)
+{
+  GbpCommentCodeViewAddin *self = GBP_COMMENT_CODE_VIEW_ADDIN (user_data);
+  IdeEditorView *editor_view = self->editor_view;
+  IdeSourceView *source_view;
+  GtkTextBuffer *buffer;
+  const gchar *param;
+  GtkSourceCompletion *completion;
+  GtkSourceLanguage *lang;
+  const gchar *start_tag;
+  const gchar *end_tag = NULL;
+  gint start_line;
+  gint end_line;
+  gint indent;
+  GtkTextIter begin;
+  GtkTextIter end;
+  gboolean editable;
+  gboolean block_comment = TRUE;
+
+  g_assert (G_IS_SIMPLE_ACTION (action));
+
+  buffer = GTK_TEXT_BUFFER (ide_editor_view_get_document (editor_view));
+  source_view = ide_editor_view_get_active_source_view (editor_view);
+  if (source_view == NULL || !GTK_SOURCE_IS_VIEW (source_view))
+    return;
+
+  editable = gtk_text_view_get_editable (GTK_TEXT_VIEW (source_view));
+  completion = gtk_source_view_get_completion (GTK_SOURCE_VIEW (source_view));
+  lang = gtk_source_buffer_get_language (GTK_SOURCE_BUFFER (buffer));
+  if (!editable || lang == NULL)
+    return;
+
+  if (ide_str_equal0 (gtk_source_language_get_id(lang), "c"))
+    {
+      start_tag = gtk_source_language_get_metadata (lang, "block-comment-start");
+      end_tag = gtk_source_language_get_metadata (lang, "block-comment-end");
+      if (start_tag == NULL || end_tag == NULL)
+        {
+          block_comment = FALSE;
+          start_tag = gtk_source_language_get_metadata (lang, "line-comment-start");
+          if (start_tag == NULL)
+            return;
+        }
+    }
+  else
+    {
+      start_tag = gtk_source_language_get_metadata (lang, "line-comment-start");
+      if (start_tag == NULL)
+        {
+          start_tag = gtk_source_language_get_metadata (lang, "block-comment-start");
+          end_tag = gtk_source_language_get_metadata (lang, "block-comment-end");
+          if (start_tag == NULL || end_tag == NULL)
+            return;
+        }
+      else
+        block_comment = FALSE;
+    }
+
+  gtk_text_buffer_get_selection_bounds (buffer, &begin, &end);
+  gtk_text_iter_order (&begin, &end);
+
+  start_line = gtk_text_iter_get_line (&begin);
+  end_line = gtk_text_iter_get_line (&end);
+
+  param = g_variant_get_string (variant, NULL);
+
+  if (*param == '0')
+    {
+      indent = get_buffer_range_min_indent (buffer, start_line, end_line);
+     if (indent == G_MAXINT)
+       return;
+
+      gtk_source_completion_block_interactive (completion);
+      gtk_text_buffer_begin_user_action (buffer);
+
+      for (gint line = start_line; line <= end_line; ++line)
+        gbp_comment_code_view_addin_comment_line (buffer, start_tag, end_tag, line, indent, block_comment);
+
+      gtk_text_buffer_end_user_action (buffer);
+      gtk_source_completion_unblock_interactive (completion);
+    }
+  else if (*param == '1')
+    {
+      gtk_source_completion_block_interactive (completion);
+      gtk_text_buffer_begin_user_action (buffer);
+
+      for (gint line = start_line; line <= end_line; ++line)
+        gbp_comment_code_view_addin_uncomment_line (buffer, start_tag, end_tag, line, block_comment);
+
+      gtk_text_buffer_end_user_action (buffer);
+      gtk_source_completion_unblock_interactive (completion);
+    }
+  else
+    g_assert_not_reached ();
+}
+
+static void
+gbp_comment_code_view_addin_load (IdeEditorViewAddin *addin,
+                                  IdeEditorView      *view)
+{
+  GbpCommentCodeViewAddin *self;
+  GtkApplication *app;
+  GActionGroup *group;
+  GSimpleAction *action;
+
+  g_assert (GBP_IS_COMMENT_CODE_VIEW_ADDIN (addin));
+  g_assert (IDE_IS_EDITOR_VIEW (view));
+
+  self = GBP_COMMENT_CODE_VIEW_ADDIN (addin);
+  self->editor_view = view;
+
+  action = g_simple_action_new ("comment-code", G_VARIANT_TYPE_STRING);
+  g_signal_connect_object (action, "activate", G_CALLBACK (gbp_comment_code_view_addin_comment_action), 
self, 0);
+
+  group = gtk_widget_get_action_group (GTK_WIDGET (view), "view");
+  g_action_map_add_action (G_ACTION_MAP (group), G_ACTION (action));
+
+  app = GTK_APPLICATION (g_application_get_default ());
+  gtk_application_set_accels_for_action (app, "view.comment-code::0", (const gchar*[]) {"<Control>m", NULL});
+  gtk_application_set_accels_for_action (app, "view.comment-code::1", (const gchar*[]) {"<Control><Shift>m", 
NULL});
+}
+
+static void
+gbp_comment_code_view_addin_unload (IdeEditorViewAddin *addin,
+                                    IdeEditorView      *view)
+{
+  GtkApplication *app;
+  GActionGroup *group;
+  const gchar *empty_accels[1] = { NULL };
+
+  g_assert (GBP_IS_COMMENT_CODE_VIEW_ADDIN (addin));
+  g_assert (IDE_IS_EDITOR_VIEW (view));
+
+  group = gtk_widget_get_action_group (GTK_WIDGET (view), "view");
+  g_action_map_remove_action (G_ACTION_MAP (group), "comment-code");
+
+  app = GTK_APPLICATION (g_application_get_default ());
+  gtk_application_set_accels_for_action (app, "view.comment-code::0", empty_accels);
+  gtk_application_set_accels_for_action (app, "view.comment-code::1", empty_accels);
+}
+
+static void
+gbp_comment_code_view_addin_class_init (GbpCommentCodeViewAddinClass *klass)
+{
+}
+
+static void
+gbp_comment_code_view_addin_init (GbpCommentCodeViewAddin *self)
+{
+}
+
+static void
+iface_init (IdeEditorViewAddinInterface *iface)
+{
+  iface->load = gbp_comment_code_view_addin_load;
+  iface->unload = gbp_comment_code_view_addin_unload;
+}
diff --git a/plugins/comment-code/gbp-comment-code-view-addin.h 
b/plugins/comment-code/gbp-comment-code-view-addin.h
new file mode 100644
index 0000000..2c2e5c6
--- /dev/null
+++ b/plugins/comment-code/gbp-comment-code-view-addin.h
@@ -0,0 +1,30 @@
+/* gbp-comment-code-view-addin.h
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome 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/>.
+ */
+
+#ifndef GBP_COMMENT_CODE_VIEW_ADDIN_H
+#define GBP_COMMENT_CODE_VIEW_ADDIN_H
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_COMMENT_CODE_VIEW_ADDIN (gbp_comment_code_view_addin_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpCommentCodeViewAddin, gbp_comment_code_view_addin, GBP, COMMENT_CODE_VIEW_ADDIN, 
GObject)
+
+G_END_DECLS
+
+#endif /* GBP_COMMENT_CODE_VIEW_ADDIN_H */
diff --git a/plugins/comment-code/gbp-comment-code.gresource.xml 
b/plugins/comment-code/gbp-comment-code.gresource.xml
new file mode 100644
index 0000000..5594a12
--- /dev/null
+++ b/plugins/comment-code/gbp-comment-code.gresource.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gnome/builder/plugins/comment-code-plugin">
+    <file>gtk/menus.ui</file>
+  </gresource>
+</gresources>
diff --git a/plugins/comment-code/gtk/menus.ui b/plugins/comment-code/gtk/menus.ui
new file mode 100644
index 0000000..8945dd1
--- /dev/null
+++ b/plugins/comment-code/gtk/menus.ui
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<interface>
+  <menu id="ide-source-view-popup-menu">
+    <section id="ide-source-view-popup-menu-selection-section">
+      <submenu id="ide-source-view-popup-menu-selection-submenu">
+        <section id="ide-source-view-popup-menu-selection-comment-code-section">
+          <attribute name="after">ide-source-view-popup-menu-line-section</attribute>
+          <item>
+            <attribute name="label" translatable="yes">Comment code</attribute>
+            <attribute name="action">view.comment-code</attribute>
+            <attribute name="target" type="s">'0'</attribute>
+          </item>
+          <item>
+            <attribute name="label" translatable="yes">Uncomment code</attribute>
+            <attribute name="action">view.comment-code</attribute>
+            <attribute name="target" type="s">'1'</attribute>
+          </item>
+        </section>
+      </submenu>
+    </section>
+  </menu>
+</interface>


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