diff --git a/data/org.mate.pluma.gschema.xml.in b/data/org.mate.pluma.gschema.xml.in
index 45dd196e3..8ee4e7ead 100644
--- a/data/org.mate.pluma.gschema.xml.in
+++ b/data/org.mate.pluma.gschema.xml.in
@@ -50,6 +50,11 @@
Autosave Interval
Number of minutes after which pluma will automatically save modified files. This will only take effect if the "Autosave" option is turned on.
+
+ true
+ Hide Tailing Newline
+ Whether pluma should hide the final newline in opened files if one was present when loading the file. Enabling this will also cause newly created files to be saved with a trailing newline by default.
+
true
Show save confirmation
diff --git a/pluma/dialogs/pluma-preferences-dialog.c b/pluma/dialogs/pluma-preferences-dialog.c
index f13a86fcd..826418004 100644
--- a/pluma/dialogs/pluma-preferences-dialog.c
+++ b/pluma/dialogs/pluma-preferences-dialog.c
@@ -121,6 +121,9 @@ struct _PlumaPreferencesDialogPrivate
GtkWidget *auto_save_checkbutton;
GtkWidget *auto_save_spinbutton;
+ /* File Format */
+ GtkWidget *hide_trailing_newline_checkbutton;
+
/* Line numbers */
GtkWidget *display_line_numbers_checkbutton;
@@ -353,6 +356,11 @@ setup_editor_page (PlumaPreferencesDialog *dlg)
dlg->priv->auto_save_spinbutton,
"value",
G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET | G_SETTINGS_BIND_NO_SENSITIVITY);
+ g_settings_bind (dlg->priv->editor_settings,
+ PLUMA_SETTINGS_HIDE_TRAILING_NEWLINE,
+ dlg->priv->hide_trailing_newline_checkbutton,
+ "active",
+ G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET);
g_settings_bind (dlg->priv->editor_settings,
PLUMA_SETTINGS_DRAWER_NEWLINE,
dlg->priv->draw_newlines_checkbutton,
@@ -1276,6 +1284,8 @@ pluma_preferences_dialog_init (PlumaPreferencesDialog *dlg)
"auto_save_checkbutton", &dlg->priv->auto_save_checkbutton,
"auto_save_spinbutton", &dlg->priv->auto_save_spinbutton,
+ "hide_trailing_newline_checkbutton", &dlg->priv->hide_trailing_newline_checkbutton,
+
"default_font_checkbutton", &dlg->priv->default_font_checkbutton,
"font_button", &dlg->priv->font_button,
"font_hbox", &dlg->priv->font_hbox,
diff --git a/pluma/dialogs/pluma-preferences-dialog.ui b/pluma/dialogs/pluma-preferences-dialog.ui
index c411f0c58..f82080ba8 100644
--- a/pluma/dialogs/pluma-preferences-dialog.ui
+++ b/pluma/dialogs/pluma-preferences-dialog.ui
@@ -1,67 +1,67 @@
-
+
diff --git a/pluma/pluma-document-input-stream.c b/pluma/pluma-document-input-stream.c
index 8bae35638..9187ed2f1 100644
--- a/pluma/pluma-document-input-stream.c
+++ b/pluma/pluma-document-input-stream.c
@@ -43,6 +43,7 @@ struct _PlumaDocumentInputStreamPrivate
gint bytes_partial;
PlumaDocumentNewlineType newline_type;
+ gboolean add_trailing_newline;
guint newline_added : 1;
guint is_initialized : 1;
@@ -54,7 +55,8 @@ enum
{
PROP_0,
PROP_BUFFER,
- PROP_NEWLINE_TYPE
+ PROP_NEWLINE_TYPE,
+ PROP_ADD_TRAILING_NEWLINE,
};
static gssize pluma_document_input_stream_read (GInputStream *stream,
@@ -84,6 +86,10 @@ pluma_document_input_stream_set_property (GObject *object,
stream->priv->newline_type = g_value_get_enum (value);
break;
+ case PROP_ADD_TRAILING_NEWLINE:
+ stream->priv->add_trailing_newline = g_value_get_boolean (value);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -108,6 +114,10 @@ pluma_document_input_stream_get_property (GObject *object,
g_value_set_enum (value, stream->priv->newline_type);
break;
+ case PROP_ADD_TRAILING_NEWLINE:
+ g_value_set_boolean (value, stream->priv->add_trailing_newline);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -152,6 +162,16 @@ pluma_document_input_stream_class_init (PlumaDocumentInputStreamClass *klass)
G_PARAM_STATIC_NAME |
G_PARAM_STATIC_BLURB |
G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (gobject_class,
+ PROP_ADD_TRAILING_NEWLINE,
+ g_param_spec_boolean ("add-trailing-newline",
+ "Add Trailing Newline",
+ "Automatically add a trailing newline to the file contents?",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT));
}
static void
@@ -428,30 +448,33 @@ pluma_document_input_stream_read (GInputStream *stream,
space_left -= n;
} while (space_left > 0 && n != 0 && dstream->priv->bytes_partial == 0);
- /* Make sure that non-empty files are always terminated with \n (see bug #95676).
- * Note that we strip the trailing \n when loading the file */
- gtk_text_buffer_get_iter_at_mark (dstream->priv->buffer,
- &iter,
- dstream->priv->pos);
-
- if (gtk_text_iter_is_end (&iter) &&
- !gtk_text_iter_is_start (&iter))
+ if (dstream->priv->add_trailing_newline)
{
- gssize newline_size;
-
- newline_size = get_new_line_size (dstream);
+ /* Make sure that non-empty files are always terminated with \n (see bug #95676).
+ * Note that we strip the trailing \n when loading the file */
+ gtk_text_buffer_get_iter_at_mark (dstream->priv->buffer,
+ &iter,
+ dstream->priv->pos);
- if (space_left >= newline_size &&
- !dstream->priv->newline_added)
+ if (gtk_text_iter_is_end (&iter) &&
+ !gtk_text_iter_is_start (&iter))
{
- const gchar *newline;
+ gssize newline_size;
+
+ newline_size = get_new_line_size (dstream);
- newline = get_new_line (dstream);
+ if (space_left >= newline_size &&
+ !dstream->priv->newline_added)
+ {
+ const gchar *newline;
+
+ newline = get_new_line (dstream);
- memcpy ((void *) ((gsize) buffer + read), newline, newline_size);
+ memcpy ((void *) ((gsize) buffer + read), newline, newline_size);
- read += newline_size;
- dstream->priv->newline_added = TRUE;
+ read += newline_size;
+ dstream->priv->newline_added = TRUE;
+ }
}
}
diff --git a/pluma/pluma-document-loader.c b/pluma/pluma-document-loader.c
index e8ceffb91..ae4de2417 100644
--- a/pluma/pluma-document-loader.c
+++ b/pluma/pluma-document-loader.c
@@ -76,7 +76,9 @@ enum
PROP_DOCUMENT,
PROP_URI,
PROP_ENCODING,
- PROP_NEWLINE_TYPE
+ PROP_NEWLINE_TYPE,
+ PROP_TRIM_TRAILING_NEWLINE,
+ PROP_TRIMMED_TRAILING_NEWLINE,
};
#define READ_CHUNK_SIZE 8192
@@ -105,6 +107,7 @@ struct _PlumaDocumentLoaderPrivate
PlumaDocumentNewlineType auto_detected_newline_type;
GFile *gfile;
goffset bytes_read;
+ gboolean trim_trailing_newline;
/* Handle for remote files */
GCancellable *cancellable;
@@ -144,6 +147,9 @@ pluma_document_loader_set_property (GObject *object,
case PROP_NEWLINE_TYPE:
loader->priv->auto_detected_newline_type = g_value_get_enum (value);
break;
+ case PROP_TRIM_TRAILING_NEWLINE:
+ loader->priv->trim_trailing_newline = g_value_get_boolean (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -172,6 +178,23 @@ pluma_document_loader_get_property (GObject *object,
case PROP_NEWLINE_TYPE:
g_value_set_enum (value, loader->priv->auto_detected_newline_type);
break;
+ case PROP_TRIM_TRAILING_NEWLINE:
+ g_value_set_boolean (value, loader->priv->trim_trailing_newline);
+ break;
+ case PROP_TRIMMED_TRAILING_NEWLINE:
+ if (loader->priv->output != NULL)
+ {
+ gboolean trimmed_trailing_newline;
+ g_object_get (loader->priv->output,
+ "trimmed-trailing-newline", &trimmed_trailing_newline,
+ NULL);
+ g_value_set_boolean (value, trimmed_trailing_newline);
+ }
+ else
+ {
+ g_value_set_boolean (value, FALSE);
+ }
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -277,15 +300,23 @@ pluma_document_loader_class_init (PlumaDocumentLoaderClass *klass)
G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class,
- PROP_NEWLINE_TYPE,
- g_param_spec_enum ("newline-type",
- "Newline type",
- "The accepted types of line ending",
- PLUMA_TYPE_DOCUMENT_NEWLINE_TYPE,
- PLUMA_DOCUMENT_NEWLINE_TYPE_LF,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB));
+ PROP_TRIM_TRAILING_NEWLINE,
+ g_param_spec_boolean ("trim-trailing-newline",
+ "Trim Trailing Newline",
+ "Remove the final received newline from the document buffer?",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class,
+ PROP_TRIMMED_TRAILING_NEWLINE,
+ g_param_spec_boolean ("trimmed-trailing-newline",
+ "Trailing Newline Trimmed",
+ "Was the final received newline removed from the document buffer?",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
signals[LOADING] =
g_signal_new ("loading",
@@ -650,6 +681,9 @@ finish_query_info (AsyncData *async)
/* Output stream */
loader->priv->output = pluma_document_output_stream_new (loader->priv->document);
+ g_object_set (G_OBJECT (loader->priv->output),
+ "trim-trailing-newline", loader->priv->trim_trailing_newline,
+ NULL);
/* start reading */
read_file_chunk (async);
diff --git a/pluma/pluma-document-output-stream.c b/pluma/pluma-document-output-stream.c
index f7ecf5af2..2b54fd239 100644
--- a/pluma/pluma-document-output-stream.c
+++ b/pluma/pluma-document-output-stream.c
@@ -45,6 +45,9 @@ struct _PlumaDocumentOutputStreamPrivate
gchar *buffer;
gsize buflen;
+ gboolean trim_trailing_newline;
+ gboolean trimmed_trailing_newline;
+
guint is_initialized : 1;
guint is_closed : 1;
};
@@ -52,7 +55,9 @@ struct _PlumaDocumentOutputStreamPrivate
enum
{
PROP_0,
- PROP_DOCUMENT
+ PROP_DOCUMENT,
+ PROP_TRIM_TRAILING_NEWLINE,
+ PROP_TRIMMED_TRAILING_NEWLINE,
};
G_DEFINE_TYPE_WITH_PRIVATE (PlumaDocumentOutputStream, pluma_document_output_stream, G_TYPE_OUTPUT_STREAM)
@@ -85,6 +90,10 @@ pluma_document_output_stream_set_property (GObject *object,
stream->priv->doc = PLUMA_DOCUMENT (g_value_get_object (value));
break;
+ case PROP_TRIM_TRAILING_NEWLINE:
+ stream->priv->trim_trailing_newline = g_value_get_boolean (value);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -105,6 +114,14 @@ pluma_document_output_stream_get_property (GObject *object,
g_value_set_object (value, stream->priv->doc);
break;
+ case PROP_TRIM_TRAILING_NEWLINE:
+ g_value_set_boolean (value, stream->priv->trim_trailing_newline);
+ break;
+
+ case PROP_TRIMMED_TRAILING_NEWLINE:
+ g_value_set_boolean (value, stream->priv->trimmed_trailing_newline);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -166,6 +183,25 @@ pluma_document_output_stream_class_init (PlumaDocumentOutputStreamClass *klass)
PLUMA_TYPE_DOCUMENT,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (object_class,
+ PROP_TRIM_TRAILING_NEWLINE,
+ g_param_spec_boolean ("trim-trailing-newline",
+ "Trim Trailing Newline",
+ "Remove the final received newline from the document buffer?",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class,
+ PROP_TRIMMED_TRAILING_NEWLINE,
+ g_param_spec_boolean ("trimmed-trailing-newline",
+ "Trailing Newline Trimmed",
+ "Was the final received newline removed from the document buffer?",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
}
static void
@@ -176,6 +212,8 @@ pluma_document_output_stream_init (PlumaDocumentOutputStream *stream)
stream->priv->buffer = NULL;
stream->priv->buflen = 0;
+ stream->priv->trimmed_trailing_newline = FALSE;
+
stream->priv->is_initialized = FALSE;
stream->priv->is_closed = FALSE;
}
@@ -264,13 +302,18 @@ remove_ending_newline (PlumaDocumentOutputStream *stream)
gtk_text_buffer_delete (GTK_TEXT_BUFFER (stream->priv->doc),
&start,
&end);
+
+ stream->priv->trimmed_trailing_newline = TRUE;
}
}
static void
end_append_text_to_document (PlumaDocumentOutputStream *stream)
{
- remove_ending_newline (stream);
+ if (stream->priv->trim_trailing_newline)
+ {
+ remove_ending_newline (stream);
+ }
gtk_text_buffer_set_modified (GTK_TEXT_BUFFER (stream->priv->doc),
FALSE);
diff --git a/pluma/pluma-document-saver.c b/pluma/pluma-document-saver.c
index 7f11376f5..caafd254f 100644
--- a/pluma/pluma-document-saver.c
+++ b/pluma/pluma-document-saver.c
@@ -63,7 +63,8 @@ enum {
PROP_URI,
PROP_ENCODING,
PROP_NEWLINE_TYPE,
- PROP_FLAGS
+ PROP_ADD_TRAILING_NEWLINE,
+ PROP_FLAGS,
};
typedef struct
@@ -94,6 +95,7 @@ struct _PlumaDocumentSaverPrivate
gchar *uri;
const PlumaEncoding *encoding;
PlumaDocumentNewlineType newline_type;
+ gboolean add_trailing_newline;
PlumaDocumentSaveFlags flags;
@@ -139,6 +141,9 @@ pluma_document_saver_set_property (GObject *object,
case PROP_NEWLINE_TYPE:
saver->priv->newline_type = g_value_get_enum (value);
break;
+ case PROP_ADD_TRAILING_NEWLINE:
+ saver->priv->add_trailing_newline = g_value_get_boolean (value);
+ break;
case PROP_FLAGS:
saver->priv->flags = g_value_get_flags (value);
break;
@@ -170,6 +175,9 @@ pluma_document_saver_get_property (GObject *object,
case PROP_NEWLINE_TYPE:
g_value_set_enum (value, saver->priv->newline_type);
break;
+ case PROP_ADD_TRAILING_NEWLINE:
+ g_value_set_boolean (value, saver->priv->add_trailing_newline);
+ break;
case PROP_FLAGS:
g_value_set_flags (value, saver->priv->flags);
break;
@@ -309,6 +317,16 @@ pluma_document_saver_class_init (PlumaDocumentSaverClass *klass)
G_PARAM_STATIC_BLURB |
G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (object_class,
+ PROP_ADD_TRAILING_NEWLINE,
+ g_param_spec_boolean ("add-trailing-newline",
+ "Add Trailing Newline",
+ "Automatically add a trailing newline to the file contents?",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT));
+
g_object_class_install_property (object_class,
PROP_FLAGS,
g_param_spec_flags ("flags",
@@ -724,6 +742,10 @@ async_replace_ready_callback (GFile *source,
saver->priv->input = pluma_document_input_stream_new (GTK_TEXT_BUFFER (saver->priv->document),
saver->priv->newline_type);
+ g_object_set (G_OBJECT (saver->priv->input),
+ "add-trailing-newline", saver->priv->add_trailing_newline,
+ NULL);
+
saver->priv->size = pluma_document_input_stream_get_total_size (PLUMA_DOCUMENT_INPUT_STREAM (saver->priv->input));
read_file_chunk (async);
diff --git a/pluma/pluma-document.c b/pluma/pluma-document.c
index f2cb8f943..a78f6df3f 100644
--- a/pluma/pluma-document.c
+++ b/pluma/pluma-document.c
@@ -128,6 +128,7 @@ struct _PlumaDocumentPrivate
gint num_of_lines_search_text;
PlumaDocumentNewlineType newline_type;
+ gboolean hide_trailing_newline;
/* Temp data while loading */
PlumaDocumentLoader *loader;
@@ -165,7 +166,8 @@ enum {
PROP_ENCODING,
PROP_CAN_SEARCH_AGAIN,
PROP_ENABLE_SEARCH_HIGHLIGHTING,
- PROP_NEWLINE_TYPE
+ PROP_NEWLINE_TYPE,
+ PROP_HIDE_TRAILING_NEWLINE,
};
enum {
@@ -360,6 +362,9 @@ pluma_document_get_property (GObject *object,
case PROP_NEWLINE_TYPE:
g_value_set_enum (value, doc->priv->newline_type);
break;
+ case PROP_HIDE_TRAILING_NEWLINE:
+ g_value_set_boolean (value, doc->priv->hide_trailing_newline);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -392,6 +397,13 @@ pluma_document_set_property (GObject *object,
pluma_document_set_content_type (doc,
g_value_get_string (value));
break;
+ case PROP_HIDE_TRAILING_NEWLINE:
+ doc->priv->hide_trailing_newline = g_value_get_boolean (value);
+ /* XXX: This should also change whether newline is visible to the user
+ or not (ie: add or remove the newline from the buffer). Not
+ really important unless this property is actually exposed in
+ the user interface though. */
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -532,6 +544,21 @@ pluma_document_class_init (PlumaDocumentClass *klass)
G_PARAM_STATIC_NAME |
G_PARAM_STATIC_BLURB));
+ /**
+ * PlumaDocument:hide-trailing-newline:
+ *
+ * The :hide-trailing-newline property determines whether the final newline
+ * in the document (if any) is stripped on loading and automatically readded
+ * on saving the document
+ */
+ g_object_class_install_property (object_class, PROP_HIDE_TRAILING_NEWLINE,
+ g_param_spec_boolean ("hide-trailing-newline",
+ "Hide Trailing Newline",
+ "Drop trailing newline from input and add it on output",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
/* This signal is used to update the cursor position is the statusbar,
* it's emitted either when the insert mark is moved explicitely or
* when the buffer changes (insert/delete).
@@ -981,6 +1008,8 @@ pluma_document_init (PlumaDocument *doc)
doc->priv->encoding = pluma_encoding_get_utf8 ();
doc->priv->newline_type = PLUMA_DOCUMENT_NEWLINE_TYPE_DEFAULT;
+ doc->priv->hide_trailing_newline = g_settings_get_boolean (doc->priv->editor_settings,
+ PLUMA_SETTINGS_HIDE_TRAILING_NEWLINE);
undo_actions = g_settings_get_uint (doc->priv->editor_settings, PLUMA_SETTINGS_MAX_UNDO_ACTIONS);
@@ -1422,6 +1451,33 @@ document_loader_loaded (PlumaDocumentLoader *loader,
pluma_document_set_newline_type (doc,
pluma_document_loader_get_newline_type (loader));
+ if (doc->priv->hide_trailing_newline)
+ {
+ gboolean trimmed_trailing_newline;
+ gint doc_char_count;
+
+ g_object_get (loader,
+ "trimmed-trailing-newline", &trimmed_trailing_newline,
+ NULL);
+
+ doc_char_count = gtk_text_buffer_get_char_count (GTK_TEXT_BUFFER (doc));
+
+ if (!trimmed_trailing_newline && doc_char_count > 0)
+ {
+ /* Document did not contain any trailing newline, so we want to
+ change hide-trailing-newline to FALSE so that saving the
+ document doesn’t automatically add a trailing newline if it
+ wasn’t previously present. */
+ /* Note that we special-case empty documents here as these never
+ contain a trailing newline, so we cannot make any assumptions
+ on whether the omission of the trailing newline was intentional
+ or not. */
+ g_object_set (doc,
+ "hide-trailing-newline", FALSE,
+ NULL);
+ }
+ }
+
restore_cursor = g_settings_get_boolean (doc->priv->editor_settings,
PLUMA_SETTINGS_RESTORE_CURSOR_POSITION);
@@ -1541,6 +1597,10 @@ pluma_document_load_real (PlumaDocument *doc,
G_CALLBACK (document_loader_loading),
doc);
+ g_object_set (G_OBJECT (doc->priv->loader),
+ "trim-trailing-newline", doc->priv->hide_trailing_newline,
+ NULL);
+
doc->priv->create = create;
doc->priv->requested_encoding = encoding;
doc->priv->requested_line_pos = line_pos;
@@ -1695,6 +1755,10 @@ pluma_document_save_real (PlumaDocument *doc,
G_CALLBACK (document_saver_saving),
doc);
+ g_object_set (G_OBJECT (doc->priv->saver),
+ "add-trailing-newline", doc->priv->hide_trailing_newline,
+ NULL);
+
doc->priv->requested_encoding = encoding;
pluma_document_saver_save (doc->priv->saver,
diff --git a/pluma/pluma-settings.h b/pluma/pluma-settings.h
index a429c0930..508371680 100644
--- a/pluma/pluma-settings.h
+++ b/pluma/pluma-settings.h
@@ -103,6 +103,7 @@ GSList * pluma_settings_get_writable_vfs_schemes (GSettings *sett
#define PLUMA_SETTINGS_CREATE_BACKUP_COPY "create-backup-copy"
#define PLUMA_SETTINGS_AUTO_SAVE "auto-save"
#define PLUMA_SETTINGS_AUTO_SAVE_INTERVAL "auto-save-interval"
+#define PLUMA_SETTINGS_HIDE_TRAILING_NEWLINE "hide-trailing-newline"
#define PLUMA_SETTINGS_MAX_UNDO_ACTIONS "max-undo-actions"
#define PLUMA_SETTINGS_WRAP_MODE "wrap-mode"
#define PLUMA_SETTINGS_TABS_SIZE "tabs-size"