diff --git a/data/io.elementary.code.gschema.xml b/data/io.elementary.code.gschema.xml
index 2978602729..17a17fdb47 100644
--- a/data/io.elementary.code.gschema.xml
+++ b/data/io.elementary.code.gschema.xml
@@ -145,6 +145,11 @@
Whether search is cyclic
Whether text searching should cycle back to the beginning of the document after reaching the end of the document.
+
+ false
+ Whether to automatically remove trailing whitespace on saving
+ Whether trailing whitespace should be removed from a document whenever it is saved, including on autosave.
+
diff --git a/plugins/meson.build b/plugins/meson.build
index 2e8855ee1d..048dfa9bf3 100644
--- a/plugins/meson.build
+++ b/plugins/meson.build
@@ -9,7 +9,6 @@ subdir('outline')
subdir('pastebin')
subdir('preserve-indent')
subdir('spell')
-subdir('strip-trailing-save')
subdir('terminal')
subdir('vim-emulation')
subdir('word-completion')
diff --git a/plugins/strip-trailing-save/meson.build b/plugins/strip-trailing-save/meson.build
deleted file mode 100644
index 9276df73d1..0000000000
--- a/plugins/strip-trailing-save/meson.build
+++ /dev/null
@@ -1,32 +0,0 @@
-module_name = 'strip-trailing-save'
-
-module_files = [
- 'strip-trailing-save.vala'
-]
-
-module_deps = [
- codecore_dep
-]
-
-shared_module(
- module_name,
- module_files,
- dependencies: module_deps,
- install: true,
- install_dir: join_paths(pluginsdir, module_name),
-)
-
-custom_target(module_name + '.plugin_merge',
- input: module_name + '.plugin',
- output: module_name + '.plugin',
- command : [msgfmt,
- '--desktop',
- '--keyword=Description',
- '--keyword=Name',
- '-d' + join_paths(meson.source_root (), 'po', 'plugins'),
- '--template=@INPUT@',
- '-o@OUTPUT@',
- ],
- install : true,
- install_dir: join_paths(pluginsdir, module_name),
-)
diff --git a/plugins/strip-trailing-save/strip-trailing-save.plugin b/plugins/strip-trailing-save/strip-trailing-save.plugin
deleted file mode 100644
index 8096de0cc6..0000000000
--- a/plugins/strip-trailing-save/strip-trailing-save.plugin
+++ /dev/null
@@ -1,10 +0,0 @@
-[Plugin]
-Module=strip-trailing-save
-Loader=C
-IAge=2
-Name=Strip trailing whitespace
-Description=Strip trailing whitespace on save
-Icon=document-save
-Authors=Filipe Funenga
-Copyright=Copyright © 2012 Code and Euclide Developers
-Website=https://github.com/elementary/code
diff --git a/plugins/strip-trailing-save/strip-trailing-save.vala b/plugins/strip-trailing-save/strip-trailing-save.vala
deleted file mode 100644
index 9eba330060..0000000000
--- a/plugins/strip-trailing-save/strip-trailing-save.vala
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (c) 2011-2012 Mario Guerriero
- * 2018 elementary LLC.
- *
- * 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 warranties of
- * MERCHANTABILITY, SATISFACTORY QUALITY, 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
- */
-
-public class Scratch.Plugins.StripTrailSave: Peas.ExtensionBase, Peas.Activatable {
- Scratch.Services.Interface plugins;
- public Object object {owned get; construct;}
- Scratch.MainWindow main_window;
- public void update_state () {return;}
-
- /*
- * Activate plugin.
- */
- public void activate () {
- plugins = (Scratch.Services.Interface) object;
- plugins.hook_window.connect ((w) => {
- this.main_window = w;
- var action = w.actions.lookup_action ("action_save") as SimpleAction;
- action.activate.connect (on_save);
- });
- }
-
- /*
- * Deactivate plugin.
- */
- public void deactivate () {
- var action = this.main_window.actions.lookup_action ("action_save") as SimpleAction;
- action.activate.disconnect (on_save);
- }
-
- /*
- * Strip trailing spaces in document.
- */
- void on_save () {
- if (main_window.get_current_document () != null) {
- var text_view = main_window.get_current_document ().source_view;
- var source_buffer = (Gtk.SourceBuffer) text_view.buffer;
- source_buffer.begin_user_action ();
- strip_trailing_spaces (source_buffer);
- source_buffer.end_user_action ();
- }
- }
-
- /*
- * Pull the buffer into an array and then work out which parts are to
- * be deleted.
- */
- private void strip_trailing_spaces (Gtk.SourceBuffer buffer) {
- Gtk.TextIter iter;
-
- var cursor_pos = buffer.cursor_position;
- buffer.get_iter_at_offset (out iter, cursor_pos);
- var orig_line = iter.get_line ();
- var orig_offset = iter.get_line_offset ();
-
- var text = buffer.text;
-
- string[] lines = Regex.split_simple ("""[\r\n]""", text);
- if (lines.length != buffer.get_line_count ()) {
- critical ("Mismatch between line counts when stripping trailing spaces, not continuing");
- return;
- }
-
- MatchInfo info;
- Gtk.TextIter start_delete, end_delete;
- Regex whitespace;
-
- try {
- whitespace = new Regex ("[ \t]+$", 0);
- } catch (RegexError e) {
- critical ("Error while building regex to replace trailing whitespace: %s", e.message);
- return;
- }
-
- for (int line_no = 0; line_no < lines.length; line_no++) {
- if (whitespace.match (lines[line_no], 0, out info)) {
- buffer.get_iter_at_line (out start_delete, line_no);
- start_delete.forward_to_line_end ();
- end_delete = start_delete;
- end_delete.backward_chars (info.fetch (0).length);
-
- buffer.@delete (ref start_delete, ref end_delete);
- }
- }
-
- buffer.get_iter_at_line_offset (out iter, orig_line, orig_offset);
- buffer.place_cursor (iter);
- }
-}
-
-[ModuleInit]
-public void peas_register_types (GLib.TypeModule module) {
- var objmodule = module as Peas.ObjectModule;
- objmodule.register_extension_type (
- typeof (Peas.Activatable),
- typeof (Scratch.Plugins.StripTrailSave)
- );
-}
diff --git a/src/Dialogs/PreferencesDialog.vala b/src/Dialogs/PreferencesDialog.vala
index 75580a28aa..f5878982e6 100644
--- a/src/Dialogs/PreferencesDialog.vala
+++ b/src/Dialogs/PreferencesDialog.vala
@@ -63,8 +63,10 @@ namespace Scratch.Dialogs {
general_grid.attach (new SettingsSwitch ("auto-indent"), 1, 4, 2);
general_grid.attach (new SettingsLabel (_("Insert spaces instead of tabs:")), 0, 5);
general_grid.attach (new SettingsSwitch ("spaces-instead-of-tabs"), 1, 5, 2);
- general_grid.attach (new SettingsLabel (_("Tab width:")), 0, 6);
- general_grid.attach (indent_width, 1, 6, 2);
+ general_grid.attach (new SettingsLabel (_("Strip trailing whitespace:")), 0, 6);
+ general_grid.attach (new SettingsSwitch ("strip-trailing-on-save"), 1, 6, 2);
+ general_grid.attach (new SettingsLabel (_("Tab width:")), 0, 7);
+ general_grid.attach (indent_width, 1, 7, 2);
main_stack = new Gtk.Stack () {
margin = 12
@@ -82,6 +84,7 @@ namespace Scratch.Dialogs {
main_grid.attach (main_stackswitcher, 0, 0);
main_grid.attach (main_stack, 0, 1);
+
border_width = 0;
get_content_area ().add (main_grid);
@@ -157,7 +160,7 @@ namespace Scratch.Dialogs {
content.attach (show_mini_map, 1, 5, 1, 1);
content.attach (show_right_margin_label, 0, 6, 1, 1);
content.attach (show_right_margin, 1, 6, 1, 1);
- content.attach (right_margin_position, 2, 6, 1, 1);
+ content.attach (right_margin_position, 2, 7, 1, 1);
content.attach (font_header, 0, 7, 3, 1);
content.attach (use_custom_font_label , 0, 9, 1, 1);
content.attach (use_custom_font, 1, 9, 1, 1);
diff --git a/src/MainWindow.vala b/src/MainWindow.vala
index c79d8e0c28..798a955c29 100644
--- a/src/MainWindow.vala
+++ b/src/MainWindow.vala
@@ -801,7 +801,7 @@ namespace Scratch {
if (doc.is_file_temporary == true) {
action_save_as ();
} else {
- doc.save.begin ();
+ doc.save.begin (true);
}
}
}
diff --git a/src/Services/Document.vala b/src/Services/Document.vala
index 0fd47933be..919ee7093f 100644
--- a/src/Services/Document.vala
+++ b/src/Services/Document.vala
@@ -93,6 +93,7 @@ namespace Scratch.Services {
public string original_content;
private string last_save_content;
public bool saved = true;
+ private bool completion_shown = false;
private Gtk.ScrolledWindow scroll;
private Gtk.InfoBar info_bar;
@@ -193,8 +194,16 @@ namespace Scratch.Services {
}
});
- /* Create as loaded - could be new document */
- loaded = true;
+ source_view.completion.show.connect (() => {
+ completion_shown = true;
+ });
+
+ source_view.completion.hide.connect (() => {
+ completion_shown = false;
+ });
+
+ // /* Create as loaded - could be new document */
+ loaded = file == null;
ellipsize_mode = Pango.EllipsizeMode.MIDDLE;
}
@@ -233,6 +242,7 @@ namespace Scratch.Services {
public async void open (bool force = false) {
/* Loading improper files may hang so we cancel after a certain time as a fallback.
* In most cases, an error will be thrown and caught. */
+ loaded = false;
if (load_cancellable != null) { /* just in case */
load_cancellable.cancel ();
}
@@ -251,7 +261,6 @@ namespace Scratch.Services {
source_view.sensitive = false;
this.working = true;
- loaded = false;
var content_type = ContentType.from_mime_type (mime_type);
@@ -312,7 +321,6 @@ namespace Scratch.Services {
source_view.buffer.text = buffer.text;
}
- loaded = true;
} catch (Error e) {
critical (e.message);
source_view.buffer.text = "";
@@ -349,7 +357,8 @@ namespace Scratch.Services {
* (large documents take time to format/display after loading)
*/
Idle.add (() => {
- this.working = false;
+ working = false;
+ loaded = true;
return false;
});
@@ -442,12 +451,19 @@ namespace Scratch.Services {
}
public async bool save (bool force = false) {
- if (!force && (source_view.buffer.get_modified () == false || this.loaded == false)) {
+ if (completion_shown ||
+ !force && (source_view.buffer.get_modified () == false ||
+ !loaded)) {
+
return false;
}
this.create_backup ();
+ if (Scratch.settings.get_boolean ("strip-trailing-on-save") && force) {
+ strip_trailing_spaces ();
+ }
+
// Replace old content with the new one
save_cancellable.cancel ();
save_cancellable = new GLib.Cancellable ();
@@ -511,7 +527,7 @@ namespace Scratch.Services {
if (success) {
source_view.buffer.set_modified (true);
- var is_saved = yield save ();
+ var is_saved = yield save (true);
if (is_saved && is_current_file_temporary) {
try {
@@ -549,6 +565,10 @@ namespace Scratch.Services {
source_map.no_show_all = true;
scroll.vscrollbar_policy = Gtk.PolicyType.AUTOMATIC;
}
+
+ if (Scratch.settings.get_boolean ("strip-trailing-on-save")) {
+ strip_trailing_spaces ();
+ }
}
// Focus the SourceView
@@ -911,5 +931,62 @@ namespace Scratch.Services {
warning ("Folder containing the file was unmounted");
mounted = false;
}
+
+ /* Pull the buffer into an array and then work out which parts are to be deleted.
+ * Do not strip line currently being edited unless forced */
+ private void strip_trailing_spaces () {
+ if (!loaded || source_view.language == null) {
+ return;
+ }
+
+ var source_buffer = (Gtk.SourceBuffer)source_view.buffer;
+ Gtk.TextIter iter;
+
+ var cursor_pos = source_buffer.cursor_position;
+ source_buffer.get_iter_at_offset (out iter, cursor_pos);
+ var orig_line = iter.get_line ();
+ var orig_offset = iter.get_line_offset ();
+
+ var text = source_buffer.text;
+
+ string[] lines = Regex.split_simple ("""[\r\n]""", text);
+ if (lines.length == 0) { // Can legitimately happen at startup or new document
+ return;
+ }
+
+ if (lines.length != source_buffer.get_line_count ()) {
+ critical ("Mismatch between line counts when stripping trailing spaces, not continuing");
+ debug ("lines.length %u, buffer lines %u \n %s", lines.length, source_buffer.get_line_count (), text);
+ return;
+ }
+
+ MatchInfo info;
+ Gtk.TextIter start_delete, end_delete;
+ Regex whitespace;
+
+ try {
+ whitespace = new Regex ("[ \t]+$", 0);
+ } catch (RegexError e) {
+ critical ("Error while building regex to replace trailing whitespace: %s", e.message);
+ return;
+ }
+
+ for (int line_no = 0; line_no < lines.length; line_no++) {
+ if (whitespace.match (lines[line_no], 0, out info)) {
+
+ source_buffer.get_iter_at_line (out start_delete, line_no);
+ start_delete.forward_to_line_end ();
+ end_delete = start_delete;
+ end_delete.backward_chars (info.fetch (0).length);
+
+ source_buffer.begin_not_undoable_action ();
+ source_buffer.@delete (ref start_delete, ref end_delete);
+ source_buffer.end_not_undoable_action ();
+ }
+ }
+
+ source_buffer.get_iter_at_line_offset (out iter, orig_line, orig_offset);
+ source_buffer.place_cursor (iter);
+ }
}
}
diff --git a/src/Services/PluginManager.vala b/src/Services/PluginManager.vala
index c8036d5db5..dbe1c08843 100644
--- a/src/Services/PluginManager.vala
+++ b/src/Services/PluginManager.vala
@@ -1,7 +1,7 @@
// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
/***
BEGIN LICENSE
-
+
Copyright (C) 2013 Mario Guerriero
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License version 3, as published