1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-23 14:42:11 +02:00

Bug 493475 - Add an option to format edited lines when saving a file

Change-Id: I1c5c91a269f7685e2963bb26f12fdee9bb5fae97
This commit is contained in:
Sergey Prigogin 2016-05-11 18:04:22 -07:00 committed by Gerrit Code Review @ Eclipse.org
parent 0d7ee343e0
commit efd6a7263b
5 changed files with 153 additions and 112 deletions

View file

@ -14,8 +14,10 @@ package org.eclipse.cdt.internal.ui.editor;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.core.filebuffers.ITextFileBuffer;
import org.eclipse.core.resources.IFile;
@ -33,22 +35,14 @@ import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DefaultLineTracker;
import org.eclipse.jface.text.DocumentRewriteSession;
import org.eclipse.jface.text.DocumentRewriteSessionType;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentExtension;
import org.eclipse.jface.text.IDocumentExtension3;
import org.eclipse.jface.text.IDocumentExtension4;
import org.eclipse.jface.text.ILineTracker;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.TextUtilities;
import org.eclipse.jface.text.formatter.FormattingContext;
import org.eclipse.jface.text.formatter.FormattingContextProperties;
import org.eclipse.jface.text.formatter.IFormattingContext;
import org.eclipse.jface.text.formatter.MultiPassContentFormatter;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.AnnotationModelEvent;
import org.eclipse.jface.text.source.IAnnotationModel;
@ -76,7 +70,10 @@ import org.eclipse.ui.texteditor.MarkerUtilities;
import org.eclipse.ui.texteditor.ResourceMarkerAnnotationModel;
import org.eclipse.ui.texteditor.spelling.SpellingAnnotation;
import org.eclipse.cdt.core.ToolFactory;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.formatter.CodeFormatter;
import org.eclipse.cdt.core.formatter.DefaultCodeFormatterConstants;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICModelMarker;
import org.eclipse.cdt.core.model.ICProject;
@ -92,7 +89,6 @@ import org.eclipse.cdt.ui.text.ICPartitions;
import org.eclipse.cdt.internal.core.model.TranslationUnit;
import org.eclipse.cdt.internal.ui.text.CFormattingStrategy;
import org.eclipse.cdt.internal.ui.text.IProblemRequestorExtension;
import org.eclipse.cdt.internal.ui.text.spelling.CoreSpellingProblem;
import org.eclipse.cdt.internal.ui.util.EditorUtility;
@ -109,7 +105,7 @@ public class CDocumentProvider extends TextFileDocumentProvider {
}
/**
* Annotation representing an <code>IProblem</code>.
* Annotation representing an {@code IProblem}.
*/
static protected class ProblemAnnotation extends Annotation implements ICAnnotation {
private static final String INDEXER_ANNOTATION_TYPE= "org.eclipse.cdt.ui.indexmarker"; //$NON-NLS-1$
@ -308,7 +304,7 @@ public class CDocumentProvider extends TextFileDocumentProvider {
/**
* Annotation model dealing with c marker annotations and temporary problems.
* Also acts as a problem requestor for its translation unit. Initially inactive.
* Also acts as a problem requester for its translation unit. Initially inactive.
* Must be explicitly activated.
*/
protected static class TranslationUnitAnnotationModel extends ResourceMarkerAnnotationModel
@ -418,7 +414,7 @@ public class CDocumentProvider extends TextFileDocumentProvider {
/**
* Sets up the infrastructure necessary for problem reporting.
*
* @param insideReportingSequence <code>true</code> if this method
* @param insideReportingSequence {@code true} if this method
* call is issued from inside a reporting sequence
*/
private void internalBeginReporting(boolean insideReportingSequence) {
@ -831,7 +827,7 @@ public class CDocumentProvider extends TextFileDocumentProvider {
/**
* Tries to synthesize an ITranslationUnit out of thin air.
* @param location the file system location of the file in question
* @return a translation unit or <code>null</code>
* @return a translation unit or {@code null}
*/
private ITranslationUnit createTranslationUnit(IPath location) {
if (location == null) {
@ -847,7 +843,7 @@ public class CDocumentProvider extends TextFileDocumentProvider {
/**
* Tries to synthesize an ITranslationUnit out of thin air.
* @param uri the URI of the file in question
* @return a translation unit or <code>null</code>
* @return a translation unit or {@code null}
*/
private ITranslationUnit createTranslationUnit(URI uri) {
if (uri == null) {
@ -887,13 +883,8 @@ public class CDocumentProvider extends TextFileDocumentProvider {
try {
CoreException saveActionException= null;
ICProject cproject = null;
try {
// Project needed to obtain formatting preferences.
if (resource != null) {
cproject = CoreModel.getDefault().create(resource.getProject());
}
performSaveActions(cproject, info.fTextFileBuffer, progress.split(20));
performSaveActions(info.fCopy, info.fTextFileBuffer, progress.split(20));
} catch (CoreException e) {
saveActionException = e;
}
@ -941,42 +932,28 @@ public class CDocumentProvider extends TextFileDocumentProvider {
return null;
}
@SuppressWarnings("deprecation")
private void formatCode(ICProject project, IDocument document) {
DocumentRewriteSession fRewriteSession = null;
private TextEdit formatCode(ITranslationUnit tu, IDocument document, IRegion[] changedRegions,
IProgressMonitor monitor) {
TextEdit rootEdit = null;
if (shouldStyleFormatCode() && project != null) {
final IFormattingContext context = new FormattingContext();
try {
context.setProperty(FormattingContextProperties.CONTEXT_PREFERENCES, project.getOptions(true));
context.setProperty(FormattingContextProperties.CONTEXT_DOCUMENT, Boolean.valueOf(true));
ICProject cProject = tu.getCProject();
Map<String, Object> options = new HashMap<String, Object>(cProject.getOptions(true));
options.put(DefaultCodeFormatterConstants.FORMATTER_TRANSLATION_UNIT, tu);
CodeFormatter formatter = ToolFactory.createCodeFormatter(options);
String code = document.get();
TextEdit[] formatEdits = formatter.format(CodeFormatter.K_TRANSLATION_UNIT, code, changedRegions,
TextUtilities.getDefaultLineDelimiter(document));
final MultiPassContentFormatter formatter= new MultiPassContentFormatter(ICPartitions.C_PARTITIONING, IDocument.DEFAULT_CONTENT_TYPE);
formatter.setMasterStrategy(new CFormattingStrategy());
try {
// Begin a single editing event
if (document instanceof IDocumentExtension4) {
IDocumentExtension4 extension= (IDocumentExtension4) document;
fRewriteSession= extension.startRewriteSession(DocumentRewriteSessionType.SEQUENTIAL);
} else if (document instanceof IDocumentExtension) {
IDocumentExtension extension= (IDocumentExtension) document;
extension.startSequentialRewrite(false);
}
formatter.format(document, context);
} finally {
if (fRewriteSession != null) {
IDocumentExtension4 extension= (IDocumentExtension4) document;
extension.stopRewriteSession(fRewriteSession);
} else {
IDocumentExtension extension= (IDocumentExtension) document;
extension.stopSequentialRewrite();
}
for (TextEdit edit : formatEdits) {
if (edit != null) {
if (rootEdit == null) {
rootEdit = new MultiTextEdit();
}
} finally {
context.dispose();
rootEdit.addChild(edit);
}
}
return rootEdit;
}
/**
@ -984,39 +961,55 @@ public class CDocumentProvider extends TextFileDocumentProvider {
* if the last line of the file was changed.
* @throws BadLocationException
*/
private void performSaveActions(ICProject project, ITextFileBuffer buffer, IProgressMonitor monitor)
private void performSaveActions(ITranslationUnit tu, ITextFileBuffer buffer, IProgressMonitor monitor)
throws CoreException {
if (shouldRemoveTrailingWhitespace() || shouldAddNewlineAtEof() || shouldStyleFormatCode()) {
if (shouldRemoveTrailingWhitespace() || shouldAddNewlineAtEof() || shouldFormatCode()) {
SubMonitor progress = SubMonitor.convert(monitor, 2);
IDocumentUndoManager undoManager= null;
IRegion[] changedRegions= needsChangedRegions() ?
EditorUtility.calculateChangedLineRegions(buffer, monitor) :
null;
EditorUtility.calculateChangedLineRegions(buffer, progress.split(1)) : null;
IDocument document = buffer.getDocument();
formatCode(project, document);
TextEdit edit = createSaveActionEdit(document, changedRegions);
if (edit != null) {
try {
IDocumentUndoManager manager= DocumentUndoManagerRegistry.getDocumentUndoManager(document);
manager.beginCompoundChange();
edit.apply(document);
manager.endCompoundChange();
} catch (MalformedTreeException e) {
String message= e.getMessage();
if (message == null)
message= "MalformedTreeException"; //$NON-NLS-1$
throw new CoreException(new Status(IStatus.ERROR, CUIPlugin.PLUGIN_ID, message, e));
} catch (BadLocationException e) {
String message= e.getMessage();
if (message == null)
message= "BadLocationException"; //$NON-NLS-1$
throw new CoreException(new Status(IStatus.ERROR, CUIPlugin.PLUGIN_ID, message, e));
try {
if (shouldFormatCode() && tu != null) {
if (!isLimitedFormatCode()) {
// The whole file has been formatted.
changedRegions = new IRegion[] { new Region(0, document.getLength()) };
}
TextEdit edit = formatCode(tu, document, changedRegions, progress.split(1));
if (edit != null) {
undoManager= DocumentUndoManagerRegistry.getDocumentUndoManager(document);
undoManager.beginCompoundChange();
edit.apply(document);
}
}
TextEdit edit = createTrailingWhitespaceEdit(document, changedRegions);
if (edit != null) {
if (undoManager == null) {
undoManager= DocumentUndoManagerRegistry.getDocumentUndoManager(document);
undoManager.beginCompoundChange();
}
edit.apply(document);
}
} catch (MalformedTreeException | BadLocationException e) {
String message= e.getMessage();
if (message == null)
message= e.getClass().getSimpleName();
throw new CoreException(new Status(IStatus.ERROR, CUIPlugin.PLUGIN_ID, message, e));
} finally {
if (undoManager != null)
undoManager.endCompoundChange();
}
}
}
private static boolean shouldStyleFormatCode() {
private static boolean shouldFormatCode() {
return PreferenceConstants.getPreferenceStore().getBoolean(PreferenceConstants.FORMAT_SOURCE_CODE);
}
private static boolean isLimitedFormatCode() {
return PreferenceConstants.getPreferenceStore().getBoolean(
PreferenceConstants.FORMAT_SOURCE_CODE);
PreferenceConstants.FORMAT_SOURCE_CODE_LIMIT_TO_EDITED_LINES);
}
private static boolean shouldAddNewlineAtEof() {
@ -1035,14 +1028,15 @@ public class CDocumentProvider extends TextFileDocumentProvider {
}
private static boolean needsChangedRegions() {
return shouldRemoveTrailingWhitespace() && isLimitedRemoveTrailingWhitespace();
return shouldRemoveTrailingWhitespace() && isLimitedRemoveTrailingWhitespace()
|| shouldFormatCode() && isLimitedFormatCode();
}
/**
* Creates a text edit for the save actions.
* @return a text edit, or <code>null</code> if the save actions leave the file intact.
* @return a text edit, or {@code null} if the save actions leave the file intact.
*/
private TextEdit createSaveActionEdit(IDocument document, IRegion[] changedRegions) {
private TextEdit createTrailingWhitespaceEdit(IDocument document, IRegion[] changedRegions) {
TextEdit rootEdit = null;
TextEdit lastWhitespaceEdit = null;
try {

View file

@ -182,11 +182,13 @@ public final class PreferencesMessages extends NLS {
public static String CEditorPreferencePage_typing_smartTab;
public static String CEditorPreferencePage_WorkspaceDefaultLabel;
public static String SaveActionsPreferencePage_removeTrailingWhitespace;
public static String SaveActionsPreferencePage_inEditedLines;
public static String SaveActionsPreferencePage_inAllLines;
public static String SaveActionsPreferencePage_ensureNewline;
public static String SaveActionsPreferencePage_formatSourceCode;
public static String SaveActionsPreferencePage_formatAllLines;
public static String SaveActionsPreferencePage_formatEditedLines;
public static String SaveActionsPreferencePage_removeTrailingWhitespace;
public static String SaveActionsPreferencePage_inAllLines;
public static String SaveActionsPreferencePage_inEditedLines;
public static String SaveActionsPreferencePage_ensureNewline;
public static String SmartTypingConfigurationBlock_autoclose_title;
public static String SmartTypingConfigurationBlock_autoindent_newlines;

View file

@ -113,11 +113,13 @@ CEditorPreferencePage_behaviorPage_matchingBracketColor=Matching brackets highli
CEditorPreferencePage_behaviorPage_inactiveCodeColor=Inactive code highlight
CEditorPreferencePage_behaviorPage_Color=Color:
SaveActionsPreferencePage_removeTrailingWhitespace=Remove trailing &whitespace
SaveActionsPreferencePage_inEditedLines=In &edited lines
SaveActionsPreferencePage_inAllLines=In a&ll lines
SaveActionsPreferencePage_ensureNewline=Ensure &newline at the end of file
SaveActionsPreferencePage_formatSourceCode=&Format source code
SaveActionsPreferencePage_formatAllLines=Format a&ll lines
SaveActionsPreferencePage_formatEditedLines=Format &edited lines
SaveActionsPreferencePage_removeTrailingWhitespace=Remove trailing &whitespace
SaveActionsPreferencePage_inAllLines=In all line&s
SaveActionsPreferencePage_inEditedLines=In edi&ted lines
SaveActionsPreferencePage_ensureNewline=Ensure &newline at the end of file
TemplatePreferencePage_Viewer_preview=Preview:

View file

@ -11,15 +11,21 @@
*******************************************************************************/
package org.eclipse.cdt.internal.ui.preferences;
import static org.eclipse.cdt.ui.PreferenceConstants.ENSURE_NEWLINE_AT_EOF;
import static org.eclipse.cdt.ui.PreferenceConstants.FORMAT_SOURCE_CODE;
import static org.eclipse.cdt.ui.PreferenceConstants.FORMAT_SOURCE_CODE_LIMIT_TO_EDITED_LINES;
import static org.eclipse.cdt.ui.PreferenceConstants.REMOVE_TRAILING_WHITESPACE;
import static org.eclipse.cdt.ui.PreferenceConstants.REMOVE_TRAILING_WHITESPACE_LIMIT_TO_EDITED_LINES;
import java.util.ArrayList;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.PlatformUI;
import org.eclipse.cdt.ui.PreferenceConstants;
import org.eclipse.cdt.utils.ui.controls.ControlFactory;
import org.eclipse.cdt.internal.ui.ICHelpContextIds;
@ -29,8 +35,10 @@ import org.eclipse.cdt.internal.ui.preferences.OverlayPreferenceStore.OverlayKey
* The page for configuring actions performed when a C/C++ file is saved.
*/
public class SaveActionsPreferencePage extends AbstractPreferencePage {
private Button fRadioEditedLines;
private Button fRadioAllLines;
private Button fRadioFormatAllLines;
private Button fRadioFormatEditedLines;
private Button fRadioTrailingWhitespaceAllLines;
private Button fRadioTrailingWhitespaceEditedLines;
public SaveActionsPreferencePage() {
super();
@ -41,13 +49,15 @@ public class SaveActionsPreferencePage extends AbstractPreferencePage {
ArrayList<OverlayKey> overlayKeys = new ArrayList<OverlayKey>();
overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN,
PreferenceConstants.REMOVE_TRAILING_WHITESPACE));
FORMAT_SOURCE_CODE));
overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN,
PreferenceConstants.REMOVE_TRAILING_WHITESPACE_LIMIT_TO_EDITED_LINES));
FORMAT_SOURCE_CODE_LIMIT_TO_EDITED_LINES));
overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN,
PreferenceConstants.ENSURE_NEWLINE_AT_EOF));
REMOVE_TRAILING_WHITESPACE));
overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN,
PreferenceConstants.FORMAT_SOURCE_CODE));
REMOVE_TRAILING_WHITESPACE_LIMIT_TO_EDITED_LINES));
overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN,
ENSURE_NEWLINE_AT_EOF));
OverlayPreferenceStore.OverlayKey[] keys = new OverlayPreferenceStore.OverlayKey[overlayKeys.size()];
overlayKeys.toArray(keys);
@ -79,26 +89,48 @@ public class SaveActionsPreferencePage extends AbstractPreferencePage {
Composite composite= ControlFactory.createComposite(parent, 1);
composite.setLayoutData(new GridData(GridData.FILL_BOTH));
String label = PreferencesMessages.SaveActionsPreferencePage_removeTrailingWhitespace;
Button checkboxTrailingWhitespace = addCheckBox(composite, label,
PreferenceConstants.REMOVE_TRAILING_WHITESPACE, 0);
fRadioEditedLines = addRadioButton(composite, PreferencesMessages.SaveActionsPreferencePage_inEditedLines,
PreferenceConstants.REMOVE_TRAILING_WHITESPACE_LIMIT_TO_EDITED_LINES, 0);
fRadioAllLines = addRadioButton(composite, PreferencesMessages.SaveActionsPreferencePage_inAllLines,
Button checkboxFormat = addCheckBox(composite,
PreferencesMessages.SaveActionsPreferencePage_formatSourceCode, FORMAT_SOURCE_CODE, 0);
Composite group = createRadioContainer(composite);
fRadioFormatAllLines = addRadioButton(group,
PreferencesMessages.SaveActionsPreferencePage_formatAllLines,
null, 0);
createDependency(checkboxTrailingWhitespace,
PreferenceConstants.REMOVE_TRAILING_WHITESPACE, fRadioEditedLines);
createDependency(checkboxTrailingWhitespace,
PreferenceConstants.REMOVE_TRAILING_WHITESPACE, fRadioAllLines);
fRadioFormatEditedLines = addRadioButton(group,
PreferencesMessages.SaveActionsPreferencePage_formatEditedLines,
REMOVE_TRAILING_WHITESPACE_LIMIT_TO_EDITED_LINES, 0);
createDependency(checkboxFormat, FORMAT_SOURCE_CODE, fRadioFormatAllLines);
createDependency(checkboxFormat, FORMAT_SOURCE_CODE, fRadioFormatEditedLines);
ControlFactory.createEmptySpace(composite, 1);
label = PreferencesMessages.SaveActionsPreferencePage_ensureNewline;
addCheckBox(composite, label, PreferenceConstants.ENSURE_NEWLINE_AT_EOF, 0);
Button checkboxTrailingWhitespace = addCheckBox(composite,
PreferencesMessages.SaveActionsPreferencePage_removeTrailingWhitespace,
REMOVE_TRAILING_WHITESPACE, 0);
group = createRadioContainer(composite);
fRadioTrailingWhitespaceAllLines = addRadioButton(group,
PreferencesMessages.SaveActionsPreferencePage_inAllLines,
null, 0);
fRadioTrailingWhitespaceEditedLines = addRadioButton(group,
PreferencesMessages.SaveActionsPreferencePage_inEditedLines,
REMOVE_TRAILING_WHITESPACE_LIMIT_TO_EDITED_LINES, 0);
createDependency(checkboxTrailingWhitespace, REMOVE_TRAILING_WHITESPACE,
fRadioTrailingWhitespaceAllLines);
createDependency(checkboxTrailingWhitespace, REMOVE_TRAILING_WHITESPACE,
fRadioTrailingWhitespaceEditedLines);
label = PreferencesMessages.SaveActionsPreferencePage_formatSourceCode;
addCheckBox(composite, label, PreferenceConstants.FORMAT_SOURCE_CODE, 0);
ControlFactory.createEmptySpace(composite, 1);
addCheckBox(composite, PreferencesMessages.SaveActionsPreferencePage_ensureNewline,
ENSURE_NEWLINE_AT_EOF, 0);
return composite;
}
private Composite createRadioContainer(Composite parent) {
Composite composite = ControlFactory.createComposite(parent, 1);
GridLayout layout = (GridLayout) composite.getLayout();
layout.marginHeight = 0;
layout.marginWidth = 0;
return composite;
}
@ -116,6 +148,7 @@ public class SaveActionsPreferencePage extends AbstractPreferencePage {
@Override
protected void initializeFields() {
super.initializeFields();
fRadioAllLines.setSelection(!fRadioEditedLines.getSelection());
fRadioFormatAllLines.setSelection(!fRadioFormatEditedLines.getSelection());
fRadioTrailingWhitespaceAllLines.setSelection(!fRadioTrailingWhitespaceEditedLines.getSelection());
}
}

View file

@ -990,12 +990,21 @@ public class PreferenceConstants {
public final static String REMOVE_TRAILING_WHITESPACE_LIMIT_TO_EDITED_LINES = "removeTrailingWhitespaceEditedLines"; //$NON-NLS-1$
/**
* Style format code on save
* Preference key for whether to format code when saving.
*
* @since 5.9
*/
public final static String FORMAT_SOURCE_CODE = "formatSourceCode"; //$NON-NLS-1$
/**
* Preference key controlling how FORMAT_SOURCE_CODE option is applied.
* If FORMAT_SOURCE_CODE is enabled, this option limits the scope of formatting
* to edited lines only. This option has no effect if FORMAT_SOURCE_CODE is disabled.
*
* @since 6.0
*/
public final static String FORMAT_SOURCE_CODE_LIMIT_TO_EDITED_LINES = "formatSourceCodeEditedLines"; //$NON-NLS-1$
/**
* A named preference that defines whether the hint to make hover sticky should be shown.
*
@ -2205,7 +2214,8 @@ public class PreferenceConstants {
store.setDefault(REMOVE_TRAILING_WHITESPACE, true);
store.setDefault(REMOVE_TRAILING_WHITESPACE_LIMIT_TO_EDITED_LINES, true);
store.setDefault(ENSURE_NEWLINE_AT_EOF, true);
store.setDefault(PreferenceConstants.FORMAT_SOURCE_CODE, false);
store.setDefault(FORMAT_SOURCE_CODE, false);
store.setDefault(FORMAT_SOURCE_CODE_LIMIT_TO_EDITED_LINES, true);
// Formatter profile
store.setDefault(FORMATTER_PROFILE, FormatterProfileManager.DEFAULT_PROFILE);