[gnome-builder] Retab: convert indetation between tabs and spaces



commit 2ecf02793910aa3c63b2aad767aa17c5f6a8278b
Author: Lucie Dvorakova <luci_dvorak me com>
Date:   Sat Mar 25 10:48:39 2017 +0100

    Retab: convert indetation between tabs and spaces
    
    When pasting code with diffrent indentation style then the one in the
    Indentation settings, the indentation style of the inserted code stays the
    same.
    
    A new plugin retab has been added. The retab is accessible through the
    Selection in the popup menu and converts the indentation style base on the
    Indentation setting of the file.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=766266

 configure.ac                          |    2 +
 meson_options.txt                     |    1 +
 plugins/Makefile.am                   |    1 +
 plugins/meson.build                   |    1 +
 plugins/retab/Makefile.am             |   35 +++++
 plugins/retab/configure.ac            |   12 ++
 plugins/retab/gbp-retab-plugin.c      |   30 +++++
 plugins/retab/gbp-retab-view-addin.c  |  223 +++++++++++++++++++++++++++++++++
 plugins/retab/gbp-retab-view-addin.h  |   30 +++++
 plugins/retab/gbp-retab.gresource.xml |    6 +
 plugins/retab/gtk/menus.ui            |   16 +++
 plugins/retab/meson.build             |   25 ++++
 plugins/retab/retab.plugin            |    9 ++
 13 files changed, 391 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index fd6a2bc..38debeb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -323,6 +323,7 @@ m4_include([plugins/project-tree/configure.ac])
 m4_include([plugins/python-gi-imports-completion/configure.ac])
 m4_include([plugins/python-pack/configure.ac])
 m4_include([plugins/quick-highlight/configure.ac])
+m4_include([plugins/retab/configure.ac])
 m4_include([plugins/rust-langserv/configure.ac])
 m4_include([plugins/rustup/configure.ac])
 m4_include([plugins/support/configure.ac])
@@ -609,6 +610,7 @@ echo "  Python GObject Introspection ......... : ${enable_python_gi_imports_comp
 echo "  Python Jedi Autocompletion ........... : ${enable_jedi_plugin}"
 echo "  Python Language Pack ................. : ${enable_python_pack_plugin}"
 echo "  Quick Highlight ...................... : ${enable_quick_highlight_plugin}"
+echo "  Retab................................. : ${enable_retab_plugin}"
 echo "  Rust Language Server ................. : ${enable_rust_langserv_plugin}"
 echo "  RustUp ............................... : ${enable_rustup_plugin}"
 echo "  Support .............................. : ${enable_support_plugin}"
diff --git a/meson_options.txt b/meson_options.txt
index e066b34..459669b 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -51,6 +51,7 @@ option('with_project_tree', type: 'boolean')
 option('with_python_gi_imports_completion', type: 'boolean')
 option('with_python_pack', type: 'boolean')
 option('with_quick_highlight', type: 'boolean')
+option('with_retab', type: 'boolean')
 option('with_rust_langserv', type: 'boolean')
 option('with_rustup', type: 'boolean')
 option('with_support', type: 'boolean')
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index c6347b7..88326b7 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -34,6 +34,7 @@ SUBDIRS =                            \
        python-gi-imports-completion \
        python-pack                  \
        quick-highlight              \
+       retab                        \
        rust-langserv                \
        rustup                       \
        support                      \
diff --git a/plugins/meson.build b/plugins/meson.build
index c7ddc37..0da6c26 100644
--- a/plugins/meson.build
+++ b/plugins/meson.build
@@ -45,6 +45,7 @@ subdir('project-tree')
 subdir('python-gi-imports-completion')
 subdir('python-pack')
 subdir('quick-highlight')
+subdir('retab')
 subdir('rust-langserv')
 subdir('rustup')
 subdir('support')
diff --git a/plugins/retab/Makefile.am b/plugins/retab/Makefile.am
new file mode 100644
index 0000000..9e3662a
--- /dev/null
+++ b/plugins/retab/Makefile.am
@@ -0,0 +1,35 @@
+if ENABLE_RETAB_PLUGIN
+
+DISTCLEANFILES =
+BUILT_SOURCES =
+CLEANFILES =
+EXTRA_DIST = $(plugin_DATA)
+
+plugindir = $(libdir)/gnome-builder/plugins
+plugin_LTLIBRARIES = libretab-plugin.la
+dist_plugin_DATA = retab.plugin
+
+libretab_plugin_la_SOURCES =        \
+       gbp-retab-plugin.c          \
+       gbp-retab-view-addin.c      \
+       gbp-retab-view-addin.h      \
+       $(NULL)
+
+nodist_libretab_plugin_la_SOURCES = \
+       gbp-retab-resources.c       \
+       gbp-retab-resources.h
+
+libretab_plugin_la_CFLAGS = $(PLUGIN_CFLAGS)
+libretab_plugin_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+
+glib_resources_c = gbp-retab-resources.c
+glib_resources_h = gbp-retab-resources.h
+glib_resources_xml = gbp-retab.gresource.xml
+glib_resources_namespace = gbp_retab
+include $(top_srcdir)/build/autotools/Makefile.am.gresources
+
+include $(top_srcdir)/plugins/Makefile.plugin
+
+endif
+
+-include $(top_srcdir)/git.mk
diff --git a/plugins/retab/configure.ac b/plugins/retab/configure.ac
new file mode 100644
index 0000000..a0c69cd
--- /dev/null
+++ b/plugins/retab/configure.ac
@@ -0,0 +1,12 @@
+# --enable-retab-plugin=yes/no
+AC_ARG_ENABLE([retab-plugin],
+              [AS_HELP_STRING([--enable-retab-plugin=@<:@yes/no@:>@],
+                              [Build with support for retabing lines.])],
+              [enable_retab_plugin=$enableval],
+              [enable_retab_plugin=yes])
+
+# for if ENABLE_RETAB_PLUGIN in Makefile.am
+AM_CONDITIONAL(ENABLE_RETAB_PLUGIN, test x$enable_retab_plugin != xno)
+
+# Ensure our makefile is generated by autoconf
+AC_CONFIG_FILES([plugins/retab/Makefile])
diff --git a/plugins/retab/gbp-retab-plugin.c b/plugins/retab/gbp-retab-plugin.c
new file mode 100644
index 0000000..e0aa8aa
--- /dev/null
+++ b/plugins/retab/gbp-retab-plugin.c
@@ -0,0 +1,30 @@
+/* gbp-retab-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-retab-view-addin.h"
+
+void
+peas_register_types (PeasObjectModule *module)
+{
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_EDITOR_VIEW_ADDIN,
+                                              GBP_TYPE_RETAB_VIEW_ADDIN);
+}
diff --git a/plugins/retab/gbp-retab-view-addin.c b/plugins/retab/gbp-retab-view-addin.c
new file mode 100644
index 0000000..3d0e88a
--- /dev/null
+++ b/plugins/retab/gbp-retab-view-addin.c
@@ -0,0 +1,223 @@
+
+/* gbp-retab-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/>.
+ */
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtksourceview/gtksource.h>
+#include <ide.h>
+
+#include "sourceview/ide-text-iter.h"
+
+#include "gbp-retab-view-addin.h"
+
+struct _GbpRetabViewAddin
+{
+  GObject        parent_instance;
+
+  IdeEditorView *editor_view;
+};
+
+static void editor_view_addin_iface_init (IdeEditorViewAddinInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GbpRetabViewAddin, gbp_retab_view_addin, G_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (IDE_TYPE_EDITOR_VIEW_ADDIN, editor_view_addin_iface_init))
+
+static gint
+get_buffer_range_indent (GtkTextBuffer *buffer,
+                         gint           line,
+                         gboolean       to_spaces)
+{
+  GtkTextIter iter;
+  gint 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);
+       ++indent;
+    }
+
+  return indent;
+}
+
+/* Removes indent that is mean to be changes and inserts
+ * tabs and/or spaces insted */
+static void
+gbp_retab_view_addin_retab (GtkTextBuffer *buffer,
+                            gint           line,
+                            gint           tab_width,
+                            gint           indent,
+                            gboolean       to_spaces)
+{
+  g_autoptr(GString) new_indent = g_string_new (NULL);
+  GtkTextIter iter;
+  GtkTextIter begin;
+  GtkTextIter end;
+  gint tab_num = 0;
+  gint space_num = 0;
+
+  g_assert (GTK_IS_TEXT_BUFFER (buffer));
+  g_assert (line >= 0 && line < gtk_text_buffer_get_line_count(buffer));
+  g_assert (tab_width != 0);
+  g_assert (new_indent != NULL);
+
+  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)))
+    {
+      if (gtk_text_iter_get_char (&iter) == ' ')
+        ++space_num;
+      else if (gtk_text_iter_get_char (&iter) == '\t')
+        ++tab_num;
+
+      gtk_text_iter_forward_char (&iter);
+    }
+
+  if (to_spaces)
+    {
+      for (gint tab = 0; tab < tab_num * tab_width; ++tab)
+        g_string_append_c(new_indent, ' ');
+
+      for (gint space = 0; space < space_num; ++space)
+        g_string_append_c(new_indent, ' ');
+    }
+  else
+    {
+      for (gint tab = 0; tab < tab_num + (space_num / tab_width); ++tab)
+        g_string_append_c(new_indent, '\t');
+
+      for (gint space = 0; space < space_num % tab_width; ++space)
+        g_string_append_c(new_indent, ' ');
+    }
+
+  gtk_text_buffer_get_iter_at_line(buffer, &begin, line);
+  gtk_text_buffer_get_iter_at_line_offset (buffer, &end, line, indent);
+  gtk_text_buffer_delete (buffer, &begin, &end);
+
+  if (new_indent->len)
+    gtk_text_buffer_insert (buffer, &begin, new_indent->str, new_indent->len);
+}
+
+static void
+gbp_retab_view_addin_action (GSimpleAction *action,
+                             GVariant      *variant,
+                             gpointer       user_data)
+{
+  GbpRetabViewAddin *self = GBP_RETAB_VIEW_ADDIN (user_data);
+  IdeEditorView *editor_view = self->editor_view;
+  IdeSourceView *source_view;
+  GtkTextBuffer *buffer;
+  GtkSourceCompletion *completion;
+  guint tab_width;
+  gint start_line;
+  gint end_line;
+  gint indent;
+  GtkTextIter begin;
+  GtkTextIter end;
+  gboolean editable;
+  gboolean to_spaces;
+
+  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));
+  tab_width = gtk_source_view_get_tab_width(GTK_SOURCE_VIEW (source_view));
+  to_spaces = gtk_source_view_get_insert_spaces_instead_of_tabs(GTK_SOURCE_VIEW (source_view));
+  if (!editable)
+    return;
+
+  gtk_text_buffer_get_selection_bounds (buffer, &begin, &end);
+  gtk_text_iter_order (&begin, &end);
+
+  if (!gtk_text_iter_equal (&begin, &end) &&
+      gtk_text_iter_starts_line (&end))
+    gtk_text_iter_backward_char (&end);
+
+  start_line = gtk_text_iter_get_line (&begin);
+  end_line = gtk_text_iter_get_line (&end);
+
+  gtk_source_completion_block_interactive (completion);
+  gtk_text_buffer_begin_user_action (buffer);
+
+  for (gint line = start_line; line <= end_line; ++line)
+    {
+      indent = get_buffer_range_indent (buffer, line, to_spaces);
+      if (indent > 0)
+        gbp_retab_view_addin_retab (buffer, line, tab_width, indent, to_spaces);
+    }
+
+  gtk_text_buffer_end_user_action (buffer);
+  gtk_source_completion_unblock_interactive (completion);
+}
+
+static void
+gbp_retab_view_addin_load (IdeEditorViewAddin *addin,
+                           IdeEditorView      *view)
+{
+  GbpRetabViewAddin *self;
+  GActionGroup *group;
+  GSimpleAction *action;
+
+  g_assert (GBP_IS_RETAB_VIEW_ADDIN (addin));
+  g_assert (IDE_IS_EDITOR_VIEW (view));
+
+  self = GBP_RETAB_VIEW_ADDIN (addin);
+  self->editor_view = view;
+
+  action = g_simple_action_new ("retab", NULL);
+  g_signal_connect_object (action, "activate", G_CALLBACK (gbp_retab_view_addin_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));
+}
+
+static void
+gbp_retab_view_addin_unload (IdeEditorViewAddin *addin,
+                             IdeEditorView      *view)
+{
+  GActionGroup *group;
+
+  g_assert (GBP_IS_RETAB_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), "retab");
+}
+
+static void
+gbp_retab_view_addin_class_init (GbpRetabViewAddinClass *klass)
+{
+}
+
+static void
+gbp_retab_view_addin_init (GbpRetabViewAddin *self)
+{
+}
+
+static void
+editor_view_addin_iface_init (IdeEditorViewAddinInterface *iface)
+{
+  iface->load = gbp_retab_view_addin_load;
+  iface->unload = gbp_retab_view_addin_unload;
+}
diff --git a/plugins/retab/gbp-retab-view-addin.h b/plugins/retab/gbp-retab-view-addin.h
new file mode 100644
index 0000000..8a92d9f
--- /dev/null
+++ b/plugins/retab/gbp-retab-view-addin.h
@@ -0,0 +1,30 @@
+/* gbp-retab-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_RETAB_VIEW_ADDIN_H
+#define GBP_RETAB_VIEW_ADDIN_H
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_RETAB_VIEW_ADDIN (gbp_retab_view_addin_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpRetabViewAddin, gbp_retab_view_addin, GBP, RETAB_VIEW_ADDIN, GObject)
+
+G_END_DECLS
+
+#endif /* GBP_RETAB_VIEW_ADDIN_H */
diff --git a/plugins/retab/gbp-retab.gresource.xml b/plugins/retab/gbp-retab.gresource.xml
new file mode 100644
index 0000000..02b2527
--- /dev/null
+++ b/plugins/retab/gbp-retab.gresource.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gnome/builder/plugins/retab-plugin">
+    <file>gtk/menus.ui</file>
+  </gresource>
+</gresources>
diff --git a/plugins/retab/gtk/menus.ui b/plugins/retab/gtk/menus.ui
new file mode 100644
index 0000000..09d0909
--- /dev/null
+++ b/plugins/retab/gtk/menus.ui
@@ -0,0 +1,16 @@
+<?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-retab-section">
+          <attribute name="after">ide-source-view-popup-menu-line-section</attribute>
+          <item>
+            <attribute name="label" translatable="yes">Retab</attribute>
+            <attribute name="action">view.retab</attribute>
+          </item>
+        </section>
+      </submenu>
+    </section>
+  </menu>
+</interface>
diff --git a/plugins/retab/meson.build b/plugins/retab/meson.build
new file mode 100644
index 0000000..09f0bf9
--- /dev/null
+++ b/plugins/retab/meson.build
@@ -0,0 +1,25 @@
+if get_option('with_retab')
+
+retab_resources = gnome.compile_resources(
+  'gbp-retab-resources',
+  'gbp-retab.gresource.xml',
+  c_name: 'gbp_retab',
+)
+
+retab_sources = [
+  'gbp-retab-plugin.c',
+  'gbp-retab-view-addin.c',
+  'gbp-retab-view-addin.h',
+  retab_resources[0],
+]
+
+shared_module('retab-plugin', retab_sources,
+  dependencies: plugin_deps,
+  link_args: plugin_link_args,
+  link_depends: plugin_link_deps,
+  install: true,
+  install_dir: plugindir,
+)
+install_data('retab.plugin', install_dir: plugindir)
+
+endif
diff --git a/plugins/retab/retab.plugin b/plugins/retab/retab.plugin
new file mode 100644
index 0000000..cc5da94
--- /dev/null
+++ b/plugins/retab/retab.plugin
@@ -0,0 +1,9 @@
+[Plugin]
+Module=retab-plugin
+Name=Retab
+Description=Retab lines with Builder editor.
+Authors=Lucie Charvat <luci charvat gmail com>
+Copyright=Copyright © 2017 Lucie Charvat
+Builtin=true
+Hidden=true
+Depends=editor


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