[gtksourceview] Add a sort line method to the buffer



commit ef3d335e2608c26c0e02ab97244df510bfdf1952
Author: Paolo Borelli <pborelli gnome org>
Date:   Fri May 15 23:37:06 2015 +0200

    Add a sort line method to the buffer

 docs/reference/gtksourceview-3.0-sections.txt |    2 +
 gtksourceview/gtksourcebuffer.c               |  178 +++++++++++++++++++++++++
 gtksourceview/gtksourcebuffer.h               |   24 ++++
 tests/test-buffer.c                           |   46 +++++++
 4 files changed, 250 insertions(+), 0 deletions(-)
---
diff --git a/docs/reference/gtksourceview-3.0-sections.txt b/docs/reference/gtksourceview-3.0-sections.txt
index 87c335b..8d52945 100644
--- a/docs/reference/gtksourceview-3.0-sections.txt
+++ b/docs/reference/gtksourceview-3.0-sections.txt
@@ -6,6 +6,7 @@
 GtkSourceBuffer
 GtkSourceBracketMatchType
 GtkSourceChangeCaseType
+GtkSourceSortFlags
 gtk_source_buffer_new
 gtk_source_buffer_new_with_language
 <SUBSECTION Syntax Highlighting>
@@ -44,6 +45,7 @@ gtk_source_buffer_remove_source_marks
 <SUBSECTION Other>
 gtk_source_buffer_change_case
 gtk_source_buffer_join_lines
+gtk_source_buffer_sort_lines
 gtk_source_buffer_set_implicit_trailing_newline
 gtk_source_buffer_get_implicit_trailing_newline
 <SUBSECTION Standard>
diff --git a/gtksourceview/gtksourcebuffer.c b/gtksourceview/gtksourcebuffer.c
index b97a06d..c0ffa45 100644
--- a/gtksourceview/gtksourcebuffer.c
+++ b/gtksourceview/gtksourcebuffer.c
@@ -31,6 +31,7 @@
 #include "gtksourcebuffer-private.h"
 
 #include <string.h>
+#include <stdlib.h>
 #include <gtk/gtk.h>
 
 #include "gtksourcelanguage.h"
@@ -2709,6 +2710,183 @@ gtk_source_buffer_join_lines (GtkSourceBuffer *buffer,
        gtk_text_buffer_delete_mark (text_buffer, end_mark);
 }
 
+static gchar *
+get_line_slice (GtkTextBuffer *buf,
+               gint           line)
+{
+       GtkTextIter start, end;
+
+       gtk_text_buffer_get_iter_at_line (buf, &start, line);
+       end = start;
+
+       if (!gtk_text_iter_ends_line (&start))
+       {
+               gtk_text_iter_forward_to_line_end (&end);
+       }
+
+       return gtk_text_buffer_get_slice (buf, &start, &end, TRUE);
+}
+
+typedef struct {
+       gchar *line; /* the original text to re-insert */
+       gchar *key;  /* the key to use for the comparison */
+} SortLine;
+
+static gint
+compare_line (gconstpointer aptr,
+              gconstpointer bptr)
+{
+       const SortLine *a = aptr;
+       const SortLine *b = bptr;
+
+       return g_strcmp0 (a->key, b->key);
+}
+
+static gint
+compare_line_reversed (gconstpointer aptr,
+                       gconstpointer bptr)
+{
+       const SortLine *a = aptr;
+       const SortLine *b = bptr;
+
+       return g_strcmp0 (b->key, a->key);
+}
+
+/**
+ * gtk_source_buffer_sort_lines:
+ * @buffer: a #GtkSourceBuffer.
+ * @start: a #GtkTextIter.
+ * @end: a #GtkTextIter.
+ * @flags: #GtkSourceSortFlags specifying how the sort should behave
+ * @column: sort considering the text starting at the given column
+ *
+ * Sort the lines of text between the specified iterators.
+ *
+ * Since: 3.18
+ */
+void
+gtk_source_buffer_sort_lines (GtkSourceBuffer    *buffer,
+                              GtkTextIter        *start,
+                              GtkTextIter        *end,
+                              GtkSourceSortFlags  flags,
+                              gint                column)
+{
+       GtkTextBuffer *text_buffer;
+       gint start_line;
+       gint end_line;
+       gint num_lines;
+       SortLine *lines;
+       gchar *last_line = NULL;
+       gint i;
+
+       g_return_if_fail (GTK_SOURCE_IS_BUFFER (buffer));
+       g_return_if_fail (start != NULL);
+       g_return_if_fail (end != NULL);
+
+       text_buffer = GTK_TEXT_BUFFER (buffer);
+
+       gtk_text_iter_order (start, end);
+
+       start_line = gtk_text_iter_get_line (start);
+       end_line = gtk_text_iter_get_line (end);
+
+       /* if we are at line start our last line is the previus one.
+        * Otherwise the last line is the current one but we try to
+        * move the iter after the line terminator */
+       if (gtk_text_iter_get_line_offset (end) == 0)
+       {
+               end_line = MAX (start_line, end_line - 1);
+       }
+       else
+       {
+               gtk_text_iter_forward_line (end);
+       }
+
+       num_lines = end_line - start_line + 1;
+       lines = g_new0 (SortLine, num_lines);
+
+       for (i = 0; i < num_lines; i++)
+       {
+               gchar *line;
+               gboolean free_line = FALSE;
+               glong length;
+
+               lines[i].line = get_line_slice (text_buffer, start_line + i);
+
+               if ((flags & GTK_SOURCE_SORT_FLAGS_CASE_SENSITIVE) != 0)
+               {
+                       line = lines[i].line;
+               }
+               else
+               {
+                       line = g_utf8_casefold (lines[i].line, -1);
+                       free_line = TRUE;
+               }
+
+               length = g_utf8_strlen (line, -1);
+
+               if (length < column)
+               {
+                       lines[i].key = NULL;
+               }
+               else if (column > 0)
+               {
+                       gchar *substring;
+
+                       substring = g_utf8_offset_to_pointer (line, column);
+                       lines[i].key = g_utf8_collate_key (substring, -1);
+               }
+               else
+               {
+                       lines[i].key = g_utf8_collate_key (line, -1);
+               }
+
+               if (free_line)
+               {
+                       g_free (line);
+               }
+       }
+
+       if ((flags & GTK_SOURCE_SORT_FLAGS_REVERSE_ORDER) != 0)
+       {
+               qsort (lines, num_lines, sizeof (SortLine), compare_line_reversed);
+       }
+       else
+       {
+               qsort (lines, num_lines, sizeof (SortLine), compare_line);
+       }
+
+       _gtk_source_buffer_save_and_clear_selection (buffer);
+       gtk_text_buffer_begin_user_action (text_buffer);
+
+       gtk_text_buffer_delete (text_buffer, start, end);
+
+       for (i = 0; i < num_lines; i++)
+       {
+               if ((flags & GTK_SOURCE_SORT_FLAGS_REMOVE_DUPLICATES) != 0 &&
+                   g_strcmp0 (last_line, lines[i].line) == 0)
+               {
+                       continue;
+               }
+
+               gtk_text_buffer_insert (text_buffer, start, lines[i].line, -1);
+               gtk_text_buffer_insert (text_buffer, start, "\n", -1);
+
+               last_line = lines[i].line;
+       }
+
+       gtk_text_buffer_end_user_action (text_buffer);
+       _gtk_source_buffer_restore_selection (buffer);
+
+       for (i = 0; i < num_lines; i++)
+       {
+               g_free (lines[i].line);
+               g_free (lines[i].key);
+       }
+
+       g_free (lines);
+}
+
 /**
  * gtk_source_buffer_set_undo_manager:
  * @buffer: a #GtkSourceBuffer.
diff --git a/gtksourceview/gtksourcebuffer.h b/gtksourceview/gtksourcebuffer.h
index 2f5830b..373b304 100644
--- a/gtksourceview/gtksourcebuffer.h
+++ b/gtksourceview/gtksourcebuffer.h
@@ -74,6 +74,24 @@ typedef enum
        GTK_SOURCE_CHANGE_CASE_TITLE
 } GtkSourceChangeCaseType;
 
+/**
+ * GtkSourceSortFlags:
+ * @GTK_SOURCE_SORT_FLAGS_NONE: no flags specified
+ * @GTK_SOURCE_SORT_FLAGS_CASE_SENSITIVE: case sensitive sort
+ * @GTK_SOURCE_SORT_FLAGS_REVERSE_ORDER: sort in reverse order
+ * @GTK_SOURCE_SORT_FLAGS_REMOVE_DUPLICATES: remove duplicates
+ *
+ * Since: 3.18
+ */
+typedef enum
+{
+       GTK_SOURCE_SORT_FLAGS_NONE              = 0,
+       GTK_SOURCE_SORT_FLAGS_CASE_SENSITIVE    = 1 << 0,
+       GTK_SOURCE_SORT_FLAGS_REVERSE_ORDER     = 1 << 1,
+       GTK_SOURCE_SORT_FLAGS_REMOVE_DUPLICATES = 1 << 2,
+} GtkSourceSortFlags;
+
+
 struct _GtkSourceBuffer
 {
        GtkTextBuffer parent_instance;
@@ -197,6 +215,12 @@ void                        gtk_source_buffer_join_lines                           
(GtkSourceBuffer        *buffer,
                                                                                 GtkTextIter            
*start,
                                                                                 GtkTextIter            *end);
 
+void                    gtk_source_buffer_sort_lines                           (GtkSourceBuffer        
*buffer,
+                                                                                GtkTextIter            
*start,
+                                                                                GtkTextIter            *end,
+                                                                                GtkSourceSortFlags     flags,
+                                                                                gint                   
column);
+
 GtkSourceUndoManager   *gtk_source_buffer_get_undo_manager                     (GtkSourceBuffer        
*buffer);
 
 void                    gtk_source_buffer_set_undo_manager                     (GtkSourceBuffer        
*buffer,
diff --git a/tests/test-buffer.c b/tests/test-buffer.c
index dae0ae2..fdab630 100644
--- a/tests/test-buffer.c
+++ b/tests/test-buffer.c
@@ -221,6 +221,51 @@ test_join_lines (void)
        g_object_unref (buffer);
 }
 
+static void
+do_test_sort_lines (GtkSourceBuffer    *buffer,
+                   const gchar        *text,
+                   const gchar        *expected,
+                   gint                start_offset,
+                   gint                end_offset,
+                   GtkSourceSortFlags  flags,
+                   gint                column)
+{
+       GtkTextIter start;
+       GtkTextIter end;
+       gchar *changed;
+
+       gtk_text_buffer_set_text (GTK_TEXT_BUFFER (buffer), text, -1);
+
+       gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (buffer), &start, start_offset);
+       gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (buffer), &end, end_offset);
+
+       gtk_source_buffer_sort_lines (buffer, &start, &end, flags, column);
+
+       gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (buffer), &start, &end);
+       changed = gtk_text_buffer_get_text (GTK_TEXT_BUFFER (buffer), &start, &end, TRUE);
+
+       g_assert_cmpstr (changed, ==, expected);
+
+       g_free (changed);
+}
+
+static void
+test_sort_lines (void)
+{
+       GtkSourceBuffer *buffer;
+
+       buffer = gtk_source_buffer_new (NULL);
+
+       do_test_sort_lines (buffer, "aaa\nbbb\n", "aaa\nbbb\n", 0, -1, 0, 0);
+       do_test_sort_lines (buffer, "bbb\naaa\n", "aaa\nbbb\n", 0, -1, 0, 0);
+       do_test_sort_lines (buffer, "aaa\nbbb\n", "bbb\naaa\n", 0, -1, GTK_SOURCE_SORT_FLAGS_REVERSE_ORDER, 
0);
+       do_test_sort_lines (buffer, "aaa\nbbb\naaa\n", "aaa\nbbb\n", 0, -1, 
GTK_SOURCE_SORT_FLAGS_REMOVE_DUPLICATES, 0);
+       do_test_sort_lines (buffer, "bbb\naaa\nCCC\n", "CCC\naaa\nbbb\n", 0, -1, 
GTK_SOURCE_SORT_FLAGS_CASE_SENSITIVE, 0);
+       do_test_sort_lines (buffer, "aaabbb\nbbbaaa\n", "bbbaaa\naaabbb\n", 0, -1, 0, 3);
+
+       g_object_unref (buffer);
+}
+
 int
 main (int argc, char** argv)
 {
@@ -232,6 +277,7 @@ main (int argc, char** argv)
        g_test_add_func ("/Buffer/get-context-classes", test_get_context_classes);
        g_test_add_func ("/Buffer/change-case", test_change_case);
        g_test_add_func ("/Buffer/join-lines", test_join_lines);
+       g_test_add_func ("/Buffer/sort-lines", test_sort_lines);
 
        return g_test_run();
 }


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