[gedit/docstream2: 3/16] Add newline changing support



commit 55bec7a7d19d38a6ec90fdb02fa19726bf3867e6
Author: Ignacio Casal Quinteiro <icq gnome org>
Date:   Wed Jan 20 14:59:06 2010 +0100

    Add newline changing support

 gedit/gedit-document-input-stream.c |  176 ++++++++++++++++++++++++++++-------
 gedit/gedit-document-input-stream.h |    5 +-
 gedit/gedit-document.h              |    7 ++
 gedit/gedit-gio-document-saver.c    |    3 +-
 tests/document-input-stream.c       |   79 ++++++----------
 5 files changed, 185 insertions(+), 85 deletions(-)
---
diff --git a/gedit/gedit-document-input-stream.c b/gedit/gedit-document-input-stream.c
index bd05748..7c65f5a 100644
--- a/gedit/gedit-document-input-stream.c
+++ b/gedit/gedit-document-input-stream.c
@@ -28,6 +28,7 @@
 #include <string.h>
 
 #include "gedit-document-input-stream.h"
+#include "gedit-enum-types.h"
 
 G_DEFINE_TYPE (GeditDocumentInputStream, gedit_document_input_stream, G_TYPE_INPUT_STREAM);
 
@@ -36,9 +37,7 @@ struct _GeditDocumentInputStreamPrivate
 	GtkTextBuffer *buffer;
 	GtkTextMark   *pos;
 
-	/* store the trailing line */
-	gchar *line;
-	gsize  line_len;
+	GeditDocumentNewlineType newline_type;
 
 	guint newline_added : 1;
 };
@@ -46,7 +45,8 @@ struct _GeditDocumentInputStreamPrivate
 enum
 {
 	PROP_0,
-	PROP_BUFFER
+	PROP_BUFFER,
+	PROP_NEWLINE_TYPE
 };
 
 static gssize     gedit_document_input_stream_read     (GInputStream      *stream,
@@ -61,10 +61,6 @@ static gboolean   gedit_document_input_stream_close    (GInputStream      *strea
 static void
 gedit_document_input_stream_finalize (GObject *object)
 {
-	GeditDocumentInputStream *stream = GEDIT_DOCUMENT_INPUT_STREAM (object);
-
-	g_free (stream->priv->line);
-
 	G_OBJECT_CLASS (gedit_document_input_stream_parent_class)->finalize (object);
 }
 
@@ -98,6 +94,10 @@ gedit_document_input_stream_set_property (GObject      *object,
 			stream->priv->buffer = GTK_TEXT_BUFFER (g_value_get_object (value));
 			break;
 
+		case PROP_NEWLINE_TYPE:
+			stream->priv->newline_type = g_value_get_enum (value);
+			break;
+
 		default:
 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 			break;
@@ -118,6 +118,10 @@ gedit_document_input_stream_get_property (GObject    *object,
 			g_value_set_object (value, stream->priv->buffer);
 			break;
 
+		case PROP_NEWLINE_TYPE:
+			g_value_set_enum (value, stream->priv->newline_type);
+			break;
+
 		default:
 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 			break;
@@ -148,6 +152,24 @@ gedit_document_input_stream_class_init (GeditDocumentInputStreamClass *klass)
 							      GTK_TYPE_TEXT_BUFFER,
 							      G_PARAM_READWRITE |
 							      G_PARAM_CONSTRUCT_ONLY));
+
+	/**
+	 * GeditDocumentInputStream:newline-type:
+	 *
+	 * The :newline-type property determines what is considered
+	 * as a line ending when reading complete lines from the stream.
+	 */ 
+	g_object_class_install_property (gobject_class,
+					 PROP_NEWLINE_TYPE,
+					 g_param_spec_enum ("newline-type",
+							    "Newline type",
+							    "The accepted types of line ending",
+							    GEDIT_TYPE_DOCUMENT_NEWLINE_TYPE,
+							    GEDIT_DOCUMENT_NEWLINE_TYPE_LF,
+							    G_PARAM_READWRITE |
+							    G_PARAM_STATIC_NAME |
+							    G_PARAM_STATIC_BLURB |
+							    G_PARAM_CONSTRUCT_ONLY));
 }
 
 static void
@@ -157,12 +179,29 @@ gedit_document_input_stream_init (GeditDocumentInputStream *stream)
 						    GEDIT_TYPE_DOCUMENT_INPUT_STREAM,
 						    GeditDocumentInputStreamPrivate);
 
-	stream->priv->line = NULL;
-	stream->priv->line_len = 0;
-	stream->priv->pos = NULL;
 	stream->priv->newline_added = FALSE;
 }
 
+static gsize
+get_new_line_size (GeditDocumentInputStream *stream)
+{
+	gsize ret;
+
+	switch (stream->priv->newline_type)
+	{
+		case GEDIT_DOCUMENT_NEWLINE_TYPE_CR:
+		case GEDIT_DOCUMENT_NEWLINE_TYPE_LF:
+			ret = 1;
+			break;
+
+		case GEDIT_DOCUMENT_NEWLINE_TYPE_CR_LF:
+			ret = 2;
+			break;
+	}
+
+	return ret;
+}
+
 /**
  * gedit_document_input_stream_new:
  * @buffer: a #GtkTextBuffer
@@ -172,7 +211,8 @@ gedit_document_input_stream_init (GeditDocumentInputStream *stream)
  * Returns: a new #GInputStream to read @buffer
  */
 GInputStream *
-gedit_document_input_stream_new (GtkTextBuffer *buffer)
+gedit_document_input_stream_new (GtkTextBuffer           *buffer,
+				 GeditDocumentNewlineType type)
 {
 	GeditDocumentInputStream *stream;
 
@@ -180,6 +220,7 @@ gedit_document_input_stream_new (GtkTextBuffer *buffer)
 
 	stream = g_object_new (GEDIT_TYPE_DOCUMENT_INPUT_STREAM,
 			       "buffer", buffer,
+			       "newline-type", type,
 			       NULL);
 
 	return G_INPUT_STREAM (stream);
@@ -207,6 +248,29 @@ gedit_document_input_stream_tell (GeditDocumentInputStream *stream)
 	return gtk_text_iter_get_offset (&iter);
 }
 
+static const gchar *
+get_new_line (GeditDocumentInputStream *stream)
+{
+	const gchar *ret;
+
+	switch (stream->priv->newline_type)
+	{
+		case GEDIT_DOCUMENT_NEWLINE_TYPE_CR:
+			ret = "\r";
+			break;
+
+		case GEDIT_DOCUMENT_NEWLINE_TYPE_LF:
+			ret = "\n";
+			break;
+
+		case GEDIT_DOCUMENT_NEWLINE_TYPE_CR_LF:
+			ret = "\r\n";
+			break;
+	}
+
+	return ret;
+}
+
 static gsize
 read_line (GeditDocumentInputStream *stream,
 	   gchar                    *outbuf,
@@ -214,37 +278,56 @@ read_line (GeditDocumentInputStream *stream,
 {
 	GtkTextIter start, next, end;
 	gchar *buf;
-	gsize read, bytes;
+	gsize read, bytes, newline_size;
+	const gchar *newline;
+	gboolean is_last;
 
 	gtk_text_buffer_get_iter_at_mark (stream->priv->buffer,
 					  &start,
 					  stream->priv->pos);
 
 	end = next = start;
+	newline = get_new_line (stream);
+	newline_size = get_new_line_size (stream);
 
 	if (gtk_text_iter_is_end (&start))
 		return 0;
 
 	if (!gtk_text_iter_ends_line (&end))
-		gtk_text_iter_forward_line (&end);
+		gtk_text_iter_forward_to_line_end (&end);
+
+	gtk_text_iter_forward_line (&next);
 
 	buf = gtk_text_iter_get_slice (&start, &end);
-	bytes = gtk_text_iter_get_bytes_in_line (&start);
+
+	/* the bytes of a line includes also the newline, so with the
+	   offsets we remove the newline and we add the new newline size */
+	bytes = gtk_text_iter_get_offset (&end) - gtk_text_iter_get_offset (&start);
+	is_last = gtk_text_iter_is_end (&end);
+
+	/* do not add the new newline_size if there wasn't a previous one */
+	if (!is_last)
+		bytes += newline_size;
 
 	if (bytes > space_left)
 	{
 		gchar *ptr;
 		gint offset;
+		gsize to_write;
 
+		/* we don't want to write here the newline so we write what
+		   it is missing in the line and we leave the newline for the
+		   new iteration */
+		to_write = MIN (space_left, (bytes - newline_size));
 		ptr = buf;
 
-		while ((ptr - buf) < space_left)
+		while ((ptr - buf) < to_write)
 			ptr = g_utf8_next_char (ptr);
 
-		if ((ptr - buf) > space_left)
+		if ((ptr - buf) > to_write)
 			ptr = g_utf8_prev_char (ptr);
 
-		memcpy (outbuf + read, buf, (ptr - buf));
+		memcpy (outbuf, buf, (ptr - buf));
 		read = (ptr - buf);
 
 		offset = gtk_text_iter_get_line_offset (&start);
@@ -252,10 +335,19 @@ read_line (GeditDocumentInputStream *stream,
 	}
 	else
 	{
-		memcpy (outbuf + read, buf, bytes);
-		read = bytes;
-
-		start = end;
+	
+		memcpy (outbuf, buf, bytes - newline_size);
+		read = bytes - newline_size;
+
+		/* do not add the newline if there wasn't a previous, this can
+		   happen in the last line */
+		if (!is_last)
+		{
+			memcpy (outbuf + read, newline, newline_size);
+			read += newline_size;
+		}
+
+		start = next;
 	}
 
 	g_free (buf);
@@ -275,24 +367,32 @@ gedit_document_input_stream_read (GInputStream  *stream,
 				  GError       **error)
 {
 	GeditDocumentInputStream *dstream;
-	gssize space_left, read, n;
+	gssize space_left, read, n, newline_size;
+	GtkTextIter pos;
 
 	dstream = GEDIT_DOCUMENT_INPUT_STREAM (stream);
 
+	if (count < 6)
+	{
+		g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE,
+				     "Not enougth space on destiny");
+		return -1;
+	}
+
 	if (g_cancellable_set_error_if_cancelled (cancellable, error))
 		return -1;
 
+	/* Initialize the mark to the first char in the text buffer */
 	if (dstream->priv->pos == NULL)
 	{
 		GtkTextIter start;
 
 		gtk_text_buffer_get_start_iter (dstream->priv->buffer, &start);
 
-		dstream->priv->pos = gtk_text_mark_new (NULL, TRUE);
-
-		gtk_text_buffer_add_mark (dstream->priv->buffer,
-					  dstream->priv->pos,
-					  &start);
+		dstream->priv->pos = gtk_text_buffer_create_mark (dstream->priv->buffer,
+								  NULL,
+								  &start,
+								  TRUE);
 	}
 
 	space_left = count;
@@ -307,10 +407,21 @@ gedit_document_input_stream_read (GInputStream  *stream,
 
 	/* make sure files are always terminated with \n (see bug #95676). Note
 	   that we strip the trailing \n when loading the file */
-	if (space_left > 0 && !dstream->priv->newline_added)
+	gtk_text_buffer_get_iter_at_mark (dstream->priv->buffer,
+					  &pos,
+					  dstream->priv->pos);
+
+	newline_size = get_new_line_size (dstream);
+
+	if (gtk_text_iter_is_end (&pos) && space_left >= newline_size &&
+	    !dstream->priv->newline_added)
 	{
-		memcpy (buffer, "\n", 1);
-		read++;
+		const gchar *newline;
+
+		newline = get_new_line (dstream);
+
+		memcpy (buffer + read, newline, newline_size);
+		read += newline_size;
 		dstream->priv->newline_added = TRUE;
 	}
 
@@ -324,9 +435,6 @@ gedit_document_input_stream_close (GInputStream  *stream,
 {
 	GeditDocumentInputStream *dstream = GEDIT_DOCUMENT_INPUT_STREAM (stream);
 
-	g_free (dstream->priv->line);
-	dstream->priv->line = NULL;
-	dstream->priv->line_len = 0;
 	dstream->priv->newline_added = FALSE;
 
 	return TRUE;
diff --git a/gedit/gedit-document-input-stream.h b/gedit/gedit-document-input-stream.h
index ec686fa..01f6a49 100644
--- a/gedit/gedit-document-input-stream.h
+++ b/gedit/gedit-document-input-stream.h
@@ -26,6 +26,8 @@
 #include <gio/gio.h>
 #include <gtk/gtk.h>
 
+#include "gedit-document.h"
+
 G_BEGIN_DECLS
 
 #define GEDIT_TYPE_DOCUMENT_INPUT_STREAM		(gedit_document_input_stream_get_type ())
@@ -54,7 +56,8 @@ struct _GeditDocumentInputStreamClass
 
 GType				 gedit_document_input_stream_get_type		(void) G_GNUC_CONST;
 
-GInputStream			*gedit_document_input_stream_new		(GtkTextBuffer *buffer);
+GInputStream			*gedit_document_input_stream_new		(GtkTextBuffer           *buffer,
+										 GeditDocumentNewlineType type);
 
 gsize				 gedit_document_input_stream_get_total_size	(GeditDocumentInputStream *stream);
 
diff --git a/gedit/gedit-document.h b/gedit/gedit-document.h
index 0b8fde9..6d29fea 100644
--- a/gedit/gedit-document.h
+++ b/gedit/gedit-document.h
@@ -63,6 +63,13 @@ G_BEGIN_DECLS
 
 typedef enum
 {
+	GEDIT_DOCUMENT_NEWLINE_TYPE_LF,
+	GEDIT_DOCUMENT_NEWLINE_TYPE_CR,
+	GEDIT_DOCUMENT_NEWLINE_TYPE_CR_LF
+} GeditDocumentNewlineType;
+
+typedef enum
+{
 	GEDIT_SEARCH_DONT_SET_FLAGS	= 1 << 0, 
 	GEDIT_SEARCH_ENTIRE_WORD	= 1 << 1,
 	GEDIT_SEARCH_CASE_SENSITIVE	= 1 << 2
diff --git a/gedit/gedit-gio-document-saver.c b/gedit/gedit-gio-document-saver.c
index a2d099c..57a194c 100644
--- a/gedit/gedit-gio-document-saver.c
+++ b/gedit/gedit-gio-document-saver.c
@@ -537,7 +537,8 @@ async_replace_ready_callback (GFile        *source,
 		gvsaver->priv->stream = G_OUTPUT_STREAM (file_stream);
 	}
 	
-	gvsaver->priv->input = gedit_document_input_stream_new (GTK_TEXT_BUFFER (saver->document));
+	gvsaver->priv->input = gedit_document_input_stream_new (GTK_TEXT_BUFFER (saver->document),
+								G_DATA_STREAM_NEWLINE_TYPE_LF);
 
 	gvsaver->priv->size = gedit_document_input_stream_get_total_size (GEDIT_DOCUMENT_INPUT_STREAM (gvsaver->priv->input));
 
diff --git a/tests/document-input-stream.c b/tests/document-input-stream.c
index ea2d6aa..06c7f06 100644
--- a/tests/document-input-stream.c
+++ b/tests/document-input-stream.c
@@ -27,37 +27,11 @@
 #include <glib.h>
 #include <string.h>
 
-#define TEXT_TO_TEST "hello this is some text\nsencond line of the text"
-
 static void
-test_single_read ()
-{
-	GtkTextBuffer *buf;
-	GInputStream *in;
-	gsize r;
-	GError *err = NULL;
-	gchar *b;
-
-	buf = gtk_text_buffer_new (NULL);
-
-	gtk_text_buffer_set_text (buf, TEXT_TO_TEST, -1);
-
-	b = g_malloc (200);
-	in = gedit_document_input_stream_new (buf);
-
-	r = g_input_stream_read (in, b, strlen (TEXT_TO_TEST), NULL, &err);
-
-	g_assert_no_error (err);
-
-	g_assert_cmpint (r, >, 0);
-
-	b[strlen (TEXT_TO_TEST)] = '\0';
-
-	g_assert_cmpstr (b, ==, TEXT_TO_TEST);
-}
-
-static void
-test_consecutive_read ()
+test_consecutive_read (const gchar *inbuf,
+		       const gchar *outbuf,
+		       GeditDocumentNewlineType type,
+		       gsize mem)
 {
 	GtkTextBuffer *buf;
 	GInputStream *in;
@@ -67,41 +41,48 @@ test_consecutive_read ()
 
 	buf = gtk_text_buffer_new (NULL);
 
-	gtk_text_buffer_set_text (buf, TEXT_TO_TEST, -1);
+	gtk_text_buffer_set_text (buf, inbuf, -1);
 
 	b = g_malloc (200);
-	in = gedit_document_input_stream_new (buf);
+	in = gedit_document_input_stream_new (buf, type);
 
 	n = 0;
 
 	do
 	{
-		r = g_input_stream_read (in, b + n, 6, NULL, &err);
+		r = g_input_stream_read (in, b + n, mem, NULL, &err);
 		n += r;
 		g_assert_no_error (err);
 	} while (r != 0);
 
 	g_assert_cmpint (n, >, 0);
 
-	g_assert_cmpstr (b, ==, TEXT_TO_TEST"\n");
+	g_assert_cmpstr (b, ==, outbuf);
 }
 
 static void
-test_size ()
+test_consecutive_cut_char ()
 {
-	GtkTextBuffer *buf;
-	GInputStream *in;
-	gsize size;
-
-	buf = gtk_text_buffer_new (NULL);
-
-	gtk_text_buffer_set_text (buf, TEXT_TO_TEST, -1);
-
-	in = gedit_document_input_stream_new (buf);
+	/* first \n is read then fo and then is added \r but not \n */
+	test_consecutive_read ("\nfo\nbar\n\nblah\n", "\r\nfo\r\nbar\r\n\r\nblah\r\n\r\n", GEDIT_DOCUMENT_NEWLINE_TYPE_CR_LF, 8);
+}
 
-	size = gedit_document_input_stream_get_total_size (GEDIT_DOCUMENT_INPUT_STREAM (in));
+static void
+test_consecutive_big_read ()
+{
+	test_consecutive_read ("\nfo\nbar\n\nblah\n", "\rfo\rbar\r\rblah\r\r", GEDIT_DOCUMENT_NEWLINE_TYPE_CR, 200);
+	test_consecutive_read ("\rfo\rbar\r\rblah\r", "\nfo\nbar\n\nblah\n\n", GEDIT_DOCUMENT_NEWLINE_TYPE_LF, 200);
+	test_consecutive_read ("\r\nfo\r\nbar\r\n\r\nblah\r\n", "\nfo\nbar\n\nblah\n\n", GEDIT_DOCUMENT_NEWLINE_TYPE_LF, 200);
+	test_consecutive_read ("\nfo\nbar\n\nblah\n", "\r\nfo\r\nbar\r\n\r\nblah\r\n\r\n", GEDIT_DOCUMENT_NEWLINE_TYPE_CR_LF, 200);
+}
 
-	g_assert_cmpint (size, ==, strlen (TEXT_TO_TEST));
+static void
+test_consecutive_middle_read ()
+{
+	test_consecutive_read ("\nfo\nbar\n\nblah\n", "\rfo\rbar\r\rblah\r\r", GEDIT_DOCUMENT_NEWLINE_TYPE_CR, 6);
+	test_consecutive_read ("\rfo\rbar\r\rblah\r", "\nfo\nbar\n\nblah\n\n", GEDIT_DOCUMENT_NEWLINE_TYPE_LF, 6);
+	test_consecutive_read ("\r\nfo\r\nbar\r\n\r\nblah\r\n", "\nfo\nbar\n\nblah\n\n", GEDIT_DOCUMENT_NEWLINE_TYPE_LF, 6);
+	test_consecutive_read ("\nfo\nbar\n\nblah\n", "\r\nfo\r\nbar\r\n\r\nblah\r\n\r\n", GEDIT_DOCUMENT_NEWLINE_TYPE_CR_LF, 6);
 }
 
 int main (int   argc,
@@ -110,9 +91,9 @@ int main (int   argc,
 	g_type_init ();
 	g_test_init (&argc, &argv, NULL);
 
-	g_test_add_func ("/document-input-stream/single_read", test_single_read);
-	g_test_add_func ("/document-input-stream/consecutive_read", test_consecutive_read);
-	g_test_add_func ("/document-input-stream/size", test_size);
+	g_test_add_func ("/document-input-stream/consecutive_cut_char", test_consecutive_cut_char);
+	g_test_add_func ("/document-input-stream/consecutive_big_read", test_consecutive_big_read);
+	g_test_add_func ("/document-input-stream/consecutive_middle_read", test_consecutive_middle_read);
 
 	return g_test_run ();
 }



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