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