#157561: adding option to control how trailing whitespace is removed when saving files
diff --git a/editor.lib/nbproject/project.xml b/editor.lib/nbproject/project.xml
--- a/editor.lib/nbproject/project.xml
+++ b/editor.lib/nbproject/project.xml
@@ -90,7 +90,7 @@
1
- 1.20
+ 1.33
diff --git a/editor.lib/src/org/netbeans/modules/editor/lib/TrailingWhitespaceRemove.java b/editor.lib/src/org/netbeans/modules/editor/lib/TrailingWhitespaceRemove.java
--- a/editor.lib/src/org/netbeans/modules/editor/lib/TrailingWhitespaceRemove.java
+++ b/editor.lib/src/org/netbeans/modules/editor/lib/TrailingWhitespaceRemove.java
@@ -46,6 +46,7 @@
import java.util.logging.Level;
import java.util.logging.Logger;
+import java.util.prefs.Preferences;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
@@ -57,6 +58,8 @@
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.CompoundEdit;
import org.netbeans.api.editor.EditorRegistry;
+import org.netbeans.api.editor.mimelookup.MimeLookup;
+import org.netbeans.api.editor.settings.SimpleValueNames;
import org.netbeans.editor.BaseDocument;
import org.netbeans.lib.editor.util.ArrayUtilities;
import org.netbeans.lib.editor.util.GapList;
@@ -98,6 +101,7 @@
private boolean inWhitespaceRemove;
+ @SuppressWarnings("LeakingThisInConstructor")
private TrailingWhitespaceRemove(BaseDocument doc) {
this.doc = doc;
this.docText = DocumentUtilities.getText(doc); // Persists for doc's lifetime
@@ -105,15 +109,21 @@
doc.addUpdateDocumentListener(this);
}
+ @Override
public synchronized void run(CompoundEdit compoundEdit) {
- inWhitespaceRemove = true;
- try {
- new ModsProcessor().removeWhitespace();
- NewModRegionsEdit edit = new NewModRegionsEdit();
- compoundEdit.addEdit(edit);
- edit.run();
- } finally {
- inWhitespaceRemove = false;
+ Preferences prefs = MimeLookup.getLookup(DocumentUtilities.getMimeType(doc)).lookup(Preferences.class);
+ String policy = prefs.get(SimpleValueNames.ON_SAVE_REMOVE_TRAILING_WHITESPACE, "never"); //NOI18N
+ if (!"never".equals(policy)) { //NOI18N
+ inWhitespaceRemove = true;
+ try {
+ // XXX: somehow update the processor to support removing all lines vs modified lines only
+ new ModsProcessor().removeWhitespace();
+ NewModRegionsEdit edit = new NewModRegionsEdit();
+ compoundEdit.addEdit(edit);
+ edit.run();
+ } finally {
+ inWhitespaceRemove = false;
+ }
}
}
@@ -125,6 +135,7 @@
return new GapList(3);
}
+ @Override
public void insertUpdate(DocumentEvent evt) {
CompoundEdit compoundEdit = (CompoundEdit) evt;
int offset = evt.getOffset();
@@ -146,6 +157,7 @@
}
}
+ @Override
public void removeUpdate(DocumentEvent evt) {
// Currently do not handle in any special way but
// Since there's a mod on the line there will be a diff
@@ -155,6 +167,7 @@
}
}
+ @Override
public void changedUpdate(DocumentEvent evt) {
}
diff --git a/editor.settings/manifest.mf b/editor.settings/manifest.mf
--- a/editor.settings/manifest.mf
+++ b/editor.settings/manifest.mf
@@ -1,5 +1,5 @@
Manifest-Version: 1.0
OpenIDE-Module: org.netbeans.modules.editor.settings/1
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/editor/settings/Bundle.properties
-OpenIDE-Module-Specification-Version: 1.32
+OpenIDE-Module-Specification-Version: 1.33
OpenIDE-Module-Needs: org.netbeans.api.editor.settings.implementation
diff --git a/editor.settings/src/org/netbeans/api/editor/settings/SimpleValueNames.java b/editor.settings/src/org/netbeans/api/editor/settings/SimpleValueNames.java
--- a/editor.settings/src/org/netbeans/api/editor/settings/SimpleValueNames.java
+++ b/editor.settings/src/org/netbeans/api/editor/settings/SimpleValueNames.java
@@ -424,7 +424,19 @@
* @since 1.30
*/
public static final String NON_PRINTABLE_CHARACTERS_VISIBLE = "non-printable-characters-visible"; //NOI18N
-
+
+ /**
+ * Determines whether to remove trailing whitespace when saving files and how exactly to do that.
+ * Values: java.lang.String instances
+ *
+ * - never
+ *
- always
+ *
- modified-lines
+ *
+ * @since 1.33
+ */
+ public static final String ON_SAVE_REMOVE_TRAILING_WHITESPACE = "on-save-remove-trailing-whitespace"; //NOI18N
+
@PatchedPublic
private SimpleValueNames() {
// to prevent instantialization
diff --git a/options.editor/nbproject/project.xml b/options.editor/nbproject/project.xml
--- a/options.editor/nbproject/project.xml
+++ b/options.editor/nbproject/project.xml
@@ -109,7 +109,7 @@
1
- 1.29
+ 1.33
diff --git a/options.editor/src/org/netbeans/modules/options/generaleditor/Bundle.properties b/options.editor/src/org/netbeans/modules/options/generaleditor/Bundle.properties
--- a/options.editor/src/org/netbeans/modules/options/generaleditor/Bundle.properties
+++ b/options.editor/src/org/netbeans/modules/options/generaleditor/Bundle.properties
@@ -105,4 +105,12 @@
CTL_Camel_Case_Behavior_Example=Example: Caret stops at S, T, N in "SomeTypeName" when using next/previous word actions
AN_Camel_Case_Behavior_Example=Example: Caret stops at S, T, N in "SomeTypeName" when using next/previous word actions
AD_Camel_Case_Behavior_Example=Example: Caret stops at S, T, N in "SomeTypeName" when using next/previous word actions
-
+CTL_When_Saving_Files=When Saving Files
+AN_When_Saving_Files=When Saving Files
+AD_When_Saving_Files=When Saving Files
+CTL_Remove_Trailing_Whitespace=Remove Trailing Whitespace
+AN_Remove_Trailing_Whitespace=Remove Trailing Whitespace
+AD_Remove_Trailing_Whitespace=Remove Trailing Whitespace
+RTW_never=Never
+RTW_always=Always
+RTW_modified-lines=From Modified Lines Only
diff --git a/options.editor/src/org/netbeans/modules/options/generaleditor/GeneralEditorPanel.form b/options.editor/src/org/netbeans/modules/options/generaleditor/GeneralEditorPanel.form
--- a/options.editor/src/org/netbeans/modules/options/generaleditor/GeneralEditorPanel.form
+++ b/options.editor/src/org/netbeans/modules/options/generaleditor/GeneralEditorPanel.form
@@ -1,4 +1,4 @@
-
+
diff --git a/options.editor/src/org/netbeans/modules/options/generaleditor/GeneralEditorPanel.java b/options.editor/src/org/netbeans/modules/options/generaleditor/GeneralEditorPanel.java
--- a/options.editor/src/org/netbeans/modules/options/generaleditor/GeneralEditorPanel.java
+++ b/options.editor/src/org/netbeans/modules/options/generaleditor/GeneralEditorPanel.java
@@ -48,8 +48,11 @@
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.AbstractButton;
+import javax.swing.DefaultComboBoxModel;
import javax.swing.JLabel;
+import javax.swing.JList;
import javax.swing.JPanel;
+import javax.swing.ListCellRenderer;
import org.openide.awt.Mnemonics;
import org.openide.util.NbBundle;
@@ -84,8 +87,14 @@
loc (lCamelCaseBehavior, "Camel_Case_Behavior");
loc (cbCamelCaseBehavior, "Enable_Camel_Case_In_Java");
loc (lCamelCaseBehaviorExample, "Camel_Case_Behavior_Example");
+
+ loc (lWhenSavingFiles, "When_Saving_Files");
+ loc (lRemoveTrailingWhitespace, "Remove_Trailing_Whitespace");
+ loc (cboRemoveTrailingWhitespace, "Remove_Trailing_Whitespace");
+
cbUseCodeFolding.setMnemonic(NbBundle.getMessage (GeneralEditorPanel.class, "MNEMONIC_Use_Folding").charAt(0));
-
+ cboRemoveTrailingWhitespace.setRenderer(new RemoveTrailingWhitespaceRenderer(cboRemoveTrailingWhitespace.getRenderer()));
+ cboRemoveTrailingWhitespace.setModel(new DefaultComboBoxModel(new Object [] { "never", "always", "modified-lines" })); //NOI18N
}
/** This method is called from within the constructor to
@@ -111,6 +120,10 @@
jSeparator3 = new javax.swing.JSeparator();
cbCamelCaseBehavior = new javax.swing.JCheckBox();
lCamelCaseBehaviorExample = new javax.swing.JLabel();
+ jSeparator4 = new javax.swing.JSeparator();
+ lWhenSavingFiles = new javax.swing.JLabel();
+ lRemoveTrailingWhitespace = new javax.swing.JLabel();
+ cboRemoveTrailingWhitespace = new javax.swing.JComboBox();
setForeground(new java.awt.Color(99, 130, 191));
@@ -139,37 +152,72 @@
lCamelCaseBehaviorExample.setText("Example: Caret stops at J, T, N in \"JavaTypeName\" when using next/previous word acctions");
+ lWhenSavingFiles.setText("When Saving Files");
+
+ lRemoveTrailingWhitespace.setLabelFor(cboRemoveTrailingWhitespace);
+ lRemoveTrailingWhitespace.setText("Remove Trailing Whitespace:");
+
+ cboRemoveTrailingWhitespace.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
+
org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(layout.createSequentialGroup()
- .add(lCodeFolding)
- .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
- .add(jSeparator1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 671, Short.MAX_VALUE))
- .add(layout.createSequentialGroup()
- .add(lCamelCaseBehavior)
- .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
- .add(jSeparator3, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 612, Short.MAX_VALUE))
- .add(layout.createSequentialGroup()
.addContainerGap()
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(lUseCodeFolding)
.add(lCollapseByDefault))
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
- .add(lCamelCaseBehaviorExample)
- .add(cbFoldInitialComments)
- .add(cbFoldJavadocComments)
- .add(cbUseCodeFolding)
.add(layout.createSequentialGroup()
- .add(cbFoldMethods)
- .add(18, 18, 18)
- .add(cbFoldTags))
- .add(cbFoldInnerClasses)
- .add(cbFoldImports)
- .add(cbCamelCaseBehavior))
+ .add(lRemoveTrailingWhitespace)
+ .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
+ .add(cboRemoveTrailingWhitespace, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
+ .add(lCamelCaseBehaviorExample))
.addContainerGap(40, Short.MAX_VALUE))
+ .add(layout.createSequentialGroup()
+ .add(155, 155, 155)
+ .add(cbCamelCaseBehavior)
+ .addContainerGap(391, Short.MAX_VALUE))
+ .add(layout.createSequentialGroup()
+ .add(155, 155, 155)
+ .add(cbFoldImports)
+ .addContainerGap(461, Short.MAX_VALUE))
+ .add(layout.createSequentialGroup()
+ .add(155, 155, 155)
+ .add(cbFoldInnerClasses)
+ .addContainerGap(461, Short.MAX_VALUE))
+ .add(layout.createSequentialGroup()
+ .add(155, 155, 155)
+ .add(cbFoldMethods)
+ .add(18, 18, 18)
+ .add(cbFoldTags)
+ .addContainerGap(234, Short.MAX_VALUE))
+ .add(layout.createSequentialGroup()
+ .add(155, 155, 155)
+ .add(cbUseCodeFolding)
+ .addContainerGap(585, Short.MAX_VALUE))
+ .add(layout.createSequentialGroup()
+ .add(155, 155, 155)
+ .add(cbFoldJavadocComments)
+ .addContainerGap(461, Short.MAX_VALUE))
+ .add(layout.createSequentialGroup()
+ .add(155, 155, 155)
+ .add(cbFoldInitialComments)
+ .addContainerGap(461, Short.MAX_VALUE))
+ .add(layout.createSequentialGroup()
+ .add(lWhenSavingFiles)
+ .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
+ .add(jSeparator4, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 644, Short.MAX_VALUE))
+ .add(layout.createSequentialGroup()
+ .add(lCamelCaseBehavior)
+ .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
+ .add(jSeparator3, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 622, Short.MAX_VALUE))
+ .add(layout.createSequentialGroup()
+ .add(lCodeFolding)
+ .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
+ .add(jSeparator1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 673, Short.MAX_VALUE))
);
layout.linkSize(new java.awt.Component[] {cbFoldImports, cbFoldInitialComments, cbFoldInnerClasses, cbFoldJavadocComments, cbFoldMethods}, org.jdesktop.layout.GroupLayout.HORIZONTAL);
@@ -204,10 +252,19 @@
.add(jSeparator3, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 10, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
.add(lCamelCaseBehavior))
.add(2, 2, 2)
- .add(cbCamelCaseBehavior)
+ .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING)
+ .add(layout.createSequentialGroup()
+ .add(cbCamelCaseBehavior)
+ .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
+ .add(lCamelCaseBehaviorExample)
+ .add(18, 18, 18)
+ .add(jSeparator4, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 10, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
+ .add(lWhenSavingFiles))
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
- .add(lCamelCaseBehaviorExample)
- .addContainerGap(59, Short.MAX_VALUE))
+ .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
+ .add(lRemoveTrailingWhitespace)
+ .add(cboRemoveTrailingWhitespace, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
+ .addContainerGap(67, Short.MAX_VALUE))
);
}// //GEN-END:initComponents
@@ -221,13 +278,17 @@
private javax.swing.JCheckBox cbFoldMethods;
private javax.swing.JCheckBox cbFoldTags;
private javax.swing.JCheckBox cbUseCodeFolding;
+ private javax.swing.JComboBox cboRemoveTrailingWhitespace;
private javax.swing.JSeparator jSeparator1;
private javax.swing.JSeparator jSeparator3;
+ private javax.swing.JSeparator jSeparator4;
private javax.swing.JLabel lCamelCaseBehavior;
private javax.swing.JLabel lCamelCaseBehaviorExample;
private javax.swing.JLabel lCodeFolding;
private javax.swing.JLabel lCollapseByDefault;
+ private javax.swing.JLabel lRemoveTrailingWhitespace;
private javax.swing.JLabel lUseCodeFolding;
+ private javax.swing.JLabel lWhenSavingFiles;
// End of variables declaration//GEN-END:variables
@@ -245,7 +306,7 @@
(AbstractButton) c,
loc ("CTL_" + key)
);
- } else {
+ } else if (c instanceof JLabel) {
Mnemonics.setLocalizedText (
(JLabel) c,
loc ("CTL_" + key)
@@ -267,6 +328,7 @@
cbFoldInitialComments.addActionListener (this);
cbCamelCaseBehavior.addActionListener (this);
cbFoldTags.addActionListener (this);
+ cboRemoveTrailingWhitespace.addActionListener(this);
}
// init code folding
@@ -288,7 +350,10 @@
cbCamelCaseBehavior.setEnabled(true);
cbCamelCaseBehavior.setSelected(ccJava);
}
-
+
+ // when saving files section
+ cboRemoveTrailingWhitespace.setSelectedItem(model.getRemoveTrailingWhitespace());
+
updateEnabledState ();
listen = true;
@@ -312,6 +377,9 @@
// java camel case navigation
model.setCamelCaseNavigation(cbCamelCaseBehavior.isSelected());
+ // when saving files section
+ model.setRemoveTrailingWhitespace((String)cboRemoveTrailingWhitespace.getSelectedItem());
+
changed = false;
}
@@ -327,10 +395,12 @@
return changed;
}
+ @Override
public void actionPerformed (ActionEvent e) {
if (!listen) return;
- if (e.getSource () == cbUseCodeFolding)
+ if (e.getSource () == cbUseCodeFolding) {
updateEnabledState ();
+ }
changed = true;
}
@@ -346,4 +416,24 @@
cbFoldMethods.setEnabled (useCodeFolding);
cbFoldTags.setEnabled(useCodeFolding);
}
+
+ private static final class RemoveTrailingWhitespaceRenderer implements ListCellRenderer {
+
+ private final ListCellRenderer defaultRenderer;
+
+ public RemoveTrailingWhitespaceRenderer(ListCellRenderer defaultRenderer) {
+ this.defaultRenderer = defaultRenderer;
+ }
+
+ @Override
+ public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
+ return defaultRenderer.getListCellRendererComponent(
+ list,
+ NbBundle.getMessage(GeneralEditorPanel.class, "RTW_" + value), //NOI18N
+ index,
+ isSelected,
+ cellHasFocus);
+ }
+
+ } // End of RemoveTrailingWhitespaceRendererRenderer class
}
diff --git a/options.editor/src/org/netbeans/modules/options/generaleditor/Model.java b/options.editor/src/org/netbeans/modules/options/generaleditor/Model.java
--- a/options.editor/src/org/netbeans/modules/options/generaleditor/Model.java
+++ b/options.editor/src/org/netbeans/modules/options/generaleditor/Model.java
@@ -50,6 +50,7 @@
import java.util.Set;
import java.util.prefs.Preferences;
import org.netbeans.api.editor.mimelookup.MimeLookup;
+import org.netbeans.api.editor.mimelookup.MimePath;
import org.netbeans.api.editor.settings.SimpleValueNames;
import org.netbeans.modules.editor.settings.storage.api.EditorSettings;
import org.openide.util.Lookup;
@@ -122,7 +123,17 @@
void setCamelCaseNavigation(boolean value) {
NbPreferences.root ().putBoolean("useCamelCaseStyleNavigation", value); // NOI18N
}
-
+
+ String getRemoveTrailingWhitespace() {
+ Preferences prefs = MimeLookup.getLookup(MimePath.EMPTY).lookup(Preferences.class);
+ return prefs.get(SimpleValueNames.ON_SAVE_REMOVE_TRAILING_WHITESPACE, "never");
+ }
+
+ void setRemoveTrailingWhitespace(String value) {
+ Preferences prefs = MimeLookup.getLookup(MimePath.EMPTY).lookup(Preferences.class);
+ prefs.put(SimpleValueNames.ON_SAVE_REMOVE_TRAILING_WHITESPACE, value);
+ }
+
// private helper methods ..................................................
private static final List PRIVILEDGED_MIME_TYPES = Arrays.asList(new String [] {