From 27f00a30d886dc781df348ea1cc8bd5f228c42ca Mon Sep 17 00:00:00 2001 From: Sergey Prigogin Date: Fri, 8 Aug 2014 18:26:30 -0700 Subject: [PATCH] Bug 419399 - Preference for Format with no selection The user now has a choice between formatting the entire file and formatting the statement on the current line. This patch depends on the change in platform.text https://git.eclipse.org/r/#/c/31337/ Change-Id: I6ae5b925a8ebdce0eec7ff34f7b3f84da0d9c326 Signed-off-by: Sergey Prigogin Reviewed-on: https://git.eclipse.org/r/31338 --- .../DefaultCodeFormatterConstants.java | 15 ++- .../internal/formatter/CCodeFormatter.java | 82 ++++++++++++++ core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF | 2 +- core/org.eclipse.cdt.ui/plugin.properties | 2 +- core/org.eclipse.cdt.ui/plugin.xml | 2 + core/org.eclipse.cdt.ui/pom.xml | 2 +- .../ui/dialogs/FormattingScopeDialog.java | 103 ++++++++++++++++++ .../cdt/internal/ui/dialogs/Messages.java | 29 +++++ .../internal/ui/dialogs/Messages.properties | 15 +++ .../cdt/internal/ui/editor/CEditor.java | 62 ++++++++++- .../ui/preferences/CEditorPreferencePage.java | 66 +++++++---- .../ui/preferences/PreferencesMessages.java | 6 +- .../PreferencesMessages.properties | 4 + .../eclipse/cdt/ui/PreferenceConstants.java | 41 ++++++- 14 files changed, 404 insertions(+), 27 deletions(-) create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/dialogs/FormattingScopeDialog.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/dialogs/Messages.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/dialogs/Messages.properties diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/DefaultCodeFormatterConstants.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/DefaultCodeFormatterConstants.java index 366b37ff9ed..00778a40f40 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/DefaultCodeFormatterConstants.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/DefaultCodeFormatterConstants.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2011 QNX Software Systems and others. + * Copyright (c) 2000, 2014 QNX Software Systems and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -9,6 +9,7 @@ * QNX Software Systems - Initial API and implementation * Sergey Prigogin (Google) * Anton Leherbauer (Wind River Systems) + * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.core.formatter; @@ -55,6 +56,18 @@ public class DefaultCodeFormatterConstants { */ public static final String FORMATTER_TRANSLATION_UNIT = CCorePlugin.PLUGIN_ID + ".formatter.current_translation_unit"; //$NON-NLS-1$ + /** + *
+	 * FORMATTER / Option that tells the formatter that the formatting region should be
+	 * extended to the enclosing statement boundaries.
+	 *     - option id:         "org.eclipse.cdt.core.formatter.statement_scope"
+	 *     - possible values:   object of class {@link Boolean} or {@code null} 
+	 *     - default:           null
+	 * 
+ * @since 5.9 + */ + public static final String FORMATTER_STATEMENT_SCOPE = CCorePlugin.PLUGIN_ID + ".formatter.statement_scope"; //$NON-NLS-1$ + /** *
 	 * FORMATTER / Value to set a brace location at the end of a line.
diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CCodeFormatter.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CCodeFormatter.java
index e5308f20819..5467e1568ef 100644
--- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CCodeFormatter.java
+++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CCodeFormatter.java
@@ -17,6 +17,13 @@ import java.util.HashMap;
 import java.util.Map;
 
 import org.eclipse.cdt.core.CCorePlugin;
+import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
+import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
+import org.eclipse.cdt.core.dom.ast.IASTNode;
+import org.eclipse.cdt.core.dom.ast.IASTNodeSelector;
+import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroExpansion;
+import org.eclipse.cdt.core.dom.ast.IASTPreprocessorStatement;
+import org.eclipse.cdt.core.dom.ast.IASTStatement;
 import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
 import org.eclipse.cdt.core.dom.ast.gnu.cpp.GPPLanguage;
 import org.eclipse.cdt.core.formatter.CodeFormatter;
@@ -33,6 +40,8 @@ import org.eclipse.cdt.core.parser.IScannerInfo;
 import org.eclipse.cdt.core.parser.IncludeFileContentProvider;
 import org.eclipse.cdt.core.parser.ParserUtil;
 import org.eclipse.cdt.core.parser.ScannerInfo;
+import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
+import org.eclipse.cdt.internal.core.util.TextUtil;
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IStatus;
@@ -185,6 +194,11 @@ public class CCodeFormatter extends CodeFormatter {
 			IASTTranslationUnit ast) {
 		for (int i = 0; i < regions.length; i++) {
 			IRegion region = regions[i];
+			if (region.getLength() == 0) {
+				// An empty region is replaced by the region containing the line corresponding to
+				// the offset and all statements overlapping with that line.
+				region = getLineOrStatementRegion(source, region.getOffset(), ast);
+			}
 			CodeFormatterVisitor codeFormatter =
 					new CodeFormatterVisitor(preferences, region.getOffset(), region.getLength());
 			edits[i] = codeFormatter.format(source, ast);
@@ -195,6 +209,74 @@ public class CCodeFormatter extends CodeFormatter {
 		}
 	}
 
+	/**
+	 * Returns the smallest region containing the line corresponding to the given offset and all
+	 * statements overlapping with that line.
+	 */
+	private IRegion getLineOrStatementRegion(String source, int offset, IASTTranslationUnit ast) {
+		int start = TextUtil.getLineStart(source, offset);
+		int end = TextUtil.skipToNextLine(source, offset);
+		IASTNode node = findOverlappingPreprocessorStatement(start, end, ast);
+		if (node != null) {
+			IASTFileLocation location = node.getFileLocation();
+			int nodeOffset = location.getNodeOffset();
+			if (nodeOffset < start)
+				start = nodeOffset;
+			int nodeEnd = nodeOffset + location.getNodeLength();
+			if (nodeEnd > end)
+				end = nodeEnd;
+			return new Region(start, end - start);
+		}
+		IASTNodeSelector nodeSelector = ast.getNodeSelector(null);
+		for (int pos = start; pos < end;) {
+			node = nodeSelector.findFirstContainedNode(pos, end - pos);
+			if (node != null) {
+				IASTNode containedNode = node;
+				node = CPPVisitor.findAncestorWithType(containedNode, IASTStatement.class);
+				if (node == null)
+					node = CPPVisitor.findAncestorWithType(containedNode, IASTDeclaration.class);
+				if (node == null)
+					node = CPPVisitor.findAncestorWithType(containedNode, IASTPreprocessorMacroExpansion.class);
+			}
+			if (node == null)
+				break;
+			IASTFileLocation location = node.getFileLocation();
+			int nodeOffset = location.getNodeOffset();
+			if (nodeOffset < start)
+				start = nodeOffset;
+			int nodeEnd = nodeOffset + location.getNodeLength();
+			if (nodeEnd > end)
+				end = nodeEnd;
+			pos = nodeEnd;
+		}
+		
+		return new Region(start, end - start);
+	}
+
+	private IASTNode findOverlappingPreprocessorStatement(int start, int end, IASTTranslationUnit ast) {
+		IASTPreprocessorStatement[] statements = ast.getAllPreprocessorStatements();
+		int low = 0;
+		int high = statements.length;
+		while (low < high) {
+			int mid = (low + high) >>> 1;
+			IASTPreprocessorStatement statement = statements[mid];
+			IASTFileLocation location = statement.getFileLocation();
+			if (location == null) {
+				low = mid + 1;
+			} else {
+				int statementOffset = location.getNodeOffset();
+				if (statementOffset >= end) {
+					high = mid;
+				} else if (statementOffset + location.getNodeLength() <= start) {
+					low = mid + 1;
+				} else {
+					return statement;
+				}
+			}
+		}
+		return null;
+	}
+
 	private ITranslationUnit getTranslationUnit(String source) {
 		ITranslationUnit tu= (ITranslationUnit) options.get(DefaultCodeFormatterConstants.FORMATTER_TRANSLATION_UNIT);
 		if (tu == null) {
diff --git a/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF b/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF
index 7d674f94d52..4690cd405b2 100644
--- a/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF
+++ b/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: %pluginName
 Bundle-SymbolicName: org.eclipse.cdt.ui; singleton:=true
-Bundle-Version: 5.8.1.qualifier
+Bundle-Version: 5.9.0.qualifier
 Bundle-Activator: org.eclipse.cdt.ui.CUIPlugin
 Bundle-Vendor: %providerName
 Bundle-Localization: plugin
diff --git a/core/org.eclipse.cdt.ui/plugin.properties b/core/org.eclipse.cdt.ui/plugin.properties
index adcf5ba3a28..22cf46daf96 100755
--- a/core/org.eclipse.cdt.ui/plugin.properties
+++ b/core/org.eclipse.cdt.ui/plugin.properties
@@ -589,7 +589,7 @@ preferenceKeywords.organizeincludes=include includes organize
 preferenceKeywords.todo=case sensitive task tag todo xxx fix fixme project comments
 preferenceKeywords.indexer=index skip references type macro search build configuration cache memory performance
 
-preferenceKeywords.ceditor=editor appearance navigation colors smart caret positioning highlight matching bracket inactive code foreground background save trailing whitespace newline doxygen
+preferenceKeywords.ceditor=editor appearance navigation colors smart caret positioning highlight matching bracket inactive code foreground background save trailing whitespace newline doxygen format formatting statement
 preferenceKeywords.contentassist=editor content code assist complete completion insert overwrite single proposal common prefix automatically auto activation trigger category categories separate specific word hippie template
 preferenceKeywords.saveactions=editor save trailing whitespace white space end file newline 
 preferenceKeywords.scalability=editor mode large file lines disable performance 
diff --git a/core/org.eclipse.cdt.ui/plugin.xml b/core/org.eclipse.cdt.ui/plugin.xml
index 5cfce5c2437..874483ca5e0 100644
--- a/core/org.eclipse.cdt.ui/plugin.xml
+++ b/core/org.eclipse.cdt.ui/plugin.xml
@@ -4878,6 +4878,8 @@
             
             
             
+            
+            
             
             
             
diff --git a/core/org.eclipse.cdt.ui/pom.xml b/core/org.eclipse.cdt.ui/pom.xml
index a62a27b8f53..bd872aad568 100644
--- a/core/org.eclipse.cdt.ui/pom.xml
+++ b/core/org.eclipse.cdt.ui/pom.xml
@@ -11,7 +11,7 @@
 		../../pom.xml
 	
 
-	5.8.1-SNAPSHOT
+	5.9.0-SNAPSHOT
 	org.eclipse.cdt.ui
 	eclipse-plugin
 
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/dialogs/FormattingScopeDialog.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/dialogs/FormattingScopeDialog.java
new file mode 100644
index 00000000000..e00ef306471
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/dialogs/FormattingScopeDialog.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 2014 Google, Inc and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * 	   Sergey Prigogin (Google) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.internal.ui.dialogs;
+
+import org.eclipse.jface.dialogs.StatusDialog;
+import org.eclipse.jface.layout.PixelConverter;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+
+import org.eclipse.cdt.ui.CUIPlugin;
+import org.eclipse.cdt.ui.PreferenceConstants;
+
+/**
+ * Asks user how formatting of an empty selection should be interpreted.
+ */
+public class FormattingScopeDialog extends StatusDialog {
+	private Button fDoNotAskAgainCheckBox;
+	private Button fScopeFileRadio;
+	private Button fScopeStatementRadio;
+	private IPreferenceStore preferenceStore;
+
+	public FormattingScopeDialog(Shell shell) {
+		super(shell);
+		setTitle(Messages.FormattingScopeDialog_title);
+		setHelpAvailable(false);
+		preferenceStore = CUIPlugin.getDefault().getPreferenceStore();
+	}
+
+	@Override
+	protected Control createDialogArea(Composite parent) {
+		Composite composite = (Composite) super.createDialogArea(parent);
+
+		PixelConverter converter = new PixelConverter(composite);
+
+		createLabel(composite, Messages.FormattingScopeDialog_message);
+		int indent = converter.convertHorizontalDLUsToPixels(8);
+		fScopeFileRadio =
+				createRadioButton(composite, Messages.FormattingScopeDialog_format_file, indent);
+		fScopeStatementRadio =
+				createRadioButton(composite, Messages.FormattingScopeDialog_format_statement, indent);
+		createLabel(composite, ""); // Separator //$NON-NLS-1$
+		fDoNotAskAgainCheckBox =
+				createCheckBox(composite, Messages.FormattingScopeDialog_do_not_ask_again);
+
+		String scope = preferenceStore.getString(PreferenceConstants.FORMATTING_SCOPE_FOR_EMPTY_SELECTION);
+		if (PreferenceConstants.FORMATTING_SCOPE_DOCUMENT.equals(scope)) {
+			fScopeFileRadio.setSelection(true);
+		} else {
+			fScopeStatementRadio.setSelection(true);
+		}
+	    applyDialogFont(composite);
+		return composite;
+	}
+
+	@Override
+	protected void okPressed() {
+		String scope = fScopeFileRadio.getSelection() ?
+				PreferenceConstants.FORMATTING_SCOPE_DOCUMENT : PreferenceConstants.FORMATTING_SCOPE_STATEMENT; 
+		preferenceStore.setValue(PreferenceConstants.FORMATTING_SCOPE_FOR_EMPTY_SELECTION, scope);
+		if (fDoNotAskAgainCheckBox.getSelection()) {
+			preferenceStore.setValue(PreferenceConstants.FORMATTING_CONFIRM_SCOPE_FOR_EMPTY_SELECTION, false);
+		}
+		super.okPressed();
+	}
+
+	private Button createRadioButton(Composite container, String text, int indent) {
+		return createButton(SWT.RADIO, container, text, indent);
+	}
+
+	private Button createCheckBox(Composite container, String text) {
+		return createButton(SWT.CHECK, container, text, 0);
+	}
+
+	private Button createButton(int style, Composite container, String text, int indent) {
+		Button button = new Button(container, style);
+		GridData layoutData = new GridData();
+		layoutData.horizontalIndent = indent;
+		button.setLayoutData(layoutData);
+		button.setText(text);
+		return button;
+	}
+
+	private Label createLabel(Composite container, String text) {
+		Label label = new Label(container, SWT.NONE);
+		label.setLayoutData(new GridData());
+		label.setText(text);
+		return label;
+	}
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/dialogs/Messages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/dialogs/Messages.java
new file mode 100644
index 00000000000..b71c0a3d71f
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/dialogs/Messages.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2014 Google, Inc and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * 	   Sergey Prigogin (Google) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.internal.ui.dialogs;
+
+import org.eclipse.osgi.util.NLS;
+
+class Messages extends NLS {
+	public static String FormattingScopeDialog_do_not_ask_again;
+	public static String FormattingScopeDialog_format_file;
+	public static String FormattingScopeDialog_format_statement;
+	public static String FormattingScopeDialog_message;
+	public static String FormattingScopeDialog_title;
+
+	static {
+		// Initialize resource bundle.
+		NLS.initializeMessages(Messages.class.getName(), Messages.class);
+	}
+
+	private Messages() {
+	}
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/dialogs/Messages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/dialogs/Messages.properties
new file mode 100644
index 00000000000..6f9268f6d7a
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/dialogs/Messages.properties
@@ -0,0 +1,15 @@
+###############################################################################
+# Copyright (c) 2014 Google, Inc and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# 	  Sergey Prigogin (Google) - initial API and implementation
+###############################################################################
+FormattingScopeDialog_title=Select Formatting Scope
+FormattingScopeDialog_format_file=The entire &file 
+FormattingScopeDialog_format_statement=The &statement on the current line
+FormattingScopeDialog_message=No code is selected. Choose the scope of formatting.
+FormattingScopeDialog_do_not_ask_again=&Remember this choice and do not ask again
\ No newline at end of file
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java
index ad8de793cc3..645e8bc1692 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java
@@ -37,9 +37,11 @@ import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.ListenerList;
 import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Platform;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.core.runtime.content.IContentType;
 import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.core.runtime.preferences.IPreferencesService;
 import org.eclipse.help.IContext;
 import org.eclipse.help.IContextProvider;
 import org.eclipse.jface.action.GroupMarker;
@@ -48,6 +50,7 @@ import org.eclipse.jface.action.IMenuManager;
 import org.eclipse.jface.action.IStatusLineManager;
 import org.eclipse.jface.action.MenuManager;
 import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.dialogs.Dialog;
 import org.eclipse.jface.dialogs.IDialogConstants;
 import org.eclipse.jface.dialogs.MessageDialog;
 import org.eclipse.jface.dialogs.MessageDialogWithToggle;
@@ -112,6 +115,7 @@ import org.eclipse.jface.viewers.ISelectionChangedListener;
 import org.eclipse.jface.viewers.IStructuredSelection;
 import org.eclipse.jface.viewers.SelectionChangedEvent;
 import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.window.Window;
 import org.eclipse.search.ui.actions.TextSearchGroup;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.BusyIndicator;
@@ -213,6 +217,7 @@ import org.eclipse.cdt.internal.ui.actions.StructureSelectNextAction;
 import org.eclipse.cdt.internal.ui.actions.StructureSelectPreviousAction;
 import org.eclipse.cdt.internal.ui.actions.StructureSelectionAction;
 import org.eclipse.cdt.internal.ui.actions.SurroundWithActionGroup;
+import org.eclipse.cdt.internal.ui.dialogs.FormattingScopeDialog;
 import org.eclipse.cdt.internal.ui.search.IOccurrencesFinder;
 import org.eclipse.cdt.internal.ui.search.IOccurrencesFinder.OccurrenceLocation;
 import org.eclipse.cdt.internal.ui.search.OccurrencesFinder;
@@ -358,8 +363,23 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi
 			return super.requestWidgetToken(requester, priority);
 		}
 
+		// This method is called only when the Platform version is below 4.5.
+		// TODO(sprigogin): Remove this override once compatibility with Platform 4.4 is no longer
+		// required.
 		@Override
-		public IFormattingContext createFormattingContext() {
+		protected IFormattingContext createFormattingContext() {
+			Point selectedRange = getSelectedRange();
+			return createFormattingContext(selectedRange.x, selectedRange.y, false);
+		}
+
+		// This method is called when the Platform version is 4.5 or higher.
+		// @Override
+		protected IFormattingContext createFormattingContext(int selectionOffset, int selectionLength) {
+			return createFormattingContext(selectionOffset, selectionLength, true);
+		}
+
+		private IFormattingContext createFormattingContext(int selectionOffset, int selectionLength,
+				boolean formattingScopeForEmptySelectionSupported) {
 			IFormattingContext context= new FormattingContext();
 
 			Map preferences;
@@ -383,6 +403,30 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi
 				preferences.put(DefaultCodeFormatterConstants.FORMATTER_TRANSLATION_UNIT, tu);
 		        preferences.put(DefaultCodeFormatterConstants.FORMATTER_LANGUAGE, language);
 				preferences.put(DefaultCodeFormatterConstants.FORMATTER_CURRENT_FILE, tu.getResource());
+				boolean formatWholeDocument = false;
+				if (formattingScopeForEmptySelectionSupported && selectionLength == 0) {
+					// The selection is empty. Determine how it should be interpreted.
+					IPreferencesService preferenceService = Platform.getPreferencesService();
+					boolean showDialog = preferenceService.getBoolean(CUIPlugin.PLUGIN_ID,
+							PreferenceConstants.FORMATTING_CONFIRM_SCOPE_FOR_EMPTY_SELECTION, true, null);
+					if (showDialog) {
+						if (!confirmFormattingScope()) {
+							// The user clicked Cancel. Abort the formatting operation.
+							context.dispose();
+							return null;
+						}
+					}
+					String scope = preferenceService.getString(CUIPlugin.PLUGIN_ID,
+							PreferenceConstants.FORMATTING_SCOPE_FOR_EMPTY_SELECTION,
+							PreferenceConstants.FORMATTING_SCOPE_DOCUMENT, null);
+					if (PreferenceConstants.FORMATTING_SCOPE_DOCUMENT.equals(scope)) {
+						formatWholeDocument = true;
+					}
+				}
+				if (!formatWholeDocument) {
+					context.setProperty(FormattingContextProperties.CONTEXT_REGION,
+							new Region(selectionOffset, selectionLength));
+				}
 			}
 
 			if (cProject == null) {
@@ -396,6 +440,22 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi
 
 			return context;
 		}
+
+		private boolean confirmFormattingScope() {
+			int redrawCount = 0;
+			while (!redraws()) {
+				redrawCount++;
+				setRedraw(true);
+			}
+			try {
+				Dialog dialog = new FormattingScopeDialog(getSite().getShell());
+				return dialog.open() == Window.OK;
+			} finally {
+				while (--redrawCount >= 0) {
+					setRedraw(false);
+				}
+			}
+		}
 	}
 
 	private class ExitPolicy implements IExitPolicy {
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/CEditorPreferencePage.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/CEditorPreferencePage.java
index 6ed0dd248b2..932941a4aed 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/CEditorPreferencePage.java
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/CEditorPreferencePage.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2005, 2011 IBM Corporation and others.
+ * Copyright (c) 2005, 2014 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -10,11 +10,18 @@
  *     QNX Software System
  *     Anton Leherbauer (Wind River Systems)
  *     Andrew Ferguson (Symbian)
+ *     Sergey Prigogin (Google)
  *******************************************************************************/
 package org.eclipse.cdt.internal.ui.preferences;
 
+import static org.eclipse.cdt.ui.PreferenceConstants.FORMATTING_CONFIRM_SCOPE_FOR_EMPTY_SELECTION;
+import static org.eclipse.cdt.ui.PreferenceConstants.FORMATTING_SCOPE_DOCUMENT;
+import static org.eclipse.cdt.ui.PreferenceConstants.FORMATTING_SCOPE_FOR_EMPTY_SELECTION;
+import static org.eclipse.cdt.ui.PreferenceConstants.FORMATTING_SCOPE_STATEMENT;
+
 import java.util.ArrayList;
 
+import org.eclipse.core.runtime.Platform;
 import org.eclipse.jface.layout.GridDataFactory;
 import org.eclipse.jface.preference.ColorSelector;
 import org.eclipse.jface.preference.IPreferenceStore;
@@ -37,6 +44,8 @@ import org.eclipse.swt.widgets.List;
 import org.eclipse.swt.widgets.Listener;
 import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.dialogs.PreferencesUtil;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Version;
 
 import org.eclipse.cdt.ui.PreferenceConstants;
 import org.eclipse.cdt.ui.dialogs.DocCommentOwnerComposite;
@@ -50,11 +59,10 @@ import org.eclipse.cdt.internal.ui.text.c.hover.SourceViewerInformationControl;
 import org.eclipse.cdt.internal.ui.text.contentassist.ContentAssistPreference;
 import org.eclipse.cdt.internal.ui.text.doctools.DocCommentOwnerManager;
 
-/*
- * The page for setting the editor options.
+/**
+ * The preference page for setting the editor options.
  */
 public class CEditorPreferencePage extends AbstractPreferencePage {
-
 	protected final String[][] fAppearanceColorListModel = new String[][] {
 			{PreferencesMessages.CEditorPreferencePage_behaviorPage_matchingBracketColor, CEditor.MATCHING_BRACKETS_COLOR, null },
 			{PreferencesMessages.CEditorPreferencePage_behaviorPage_inactiveCodeColor, CEditor.INACTIVE_CODE_COLOR, null },
@@ -69,14 +77,19 @@ public class CEditorPreferencePage extends AbstractPreferencePage {
 	private ColorSelector fAppearanceColorEditor;
 	private Button fAppearanceColorDefault;
 	private DocCommentOwnerComposite fDocCommentOwnerComposite;
+	// TODO(sprigogin): Remove once compatibility with Platform 4.4 is no longer required.
+	private final boolean formattingScopeForEmptySelectionSupported;
 	
 	public CEditorPreferencePage() {
 		super();
+		Bundle jfaceText = Platform.getBundle("org.eclipse.jface.text"); //$NON-NLS-1$
+		formattingScopeForEmptySelectionSupported =
+				jfaceText.getVersion().compareTo(new Version(3, 10, 0)) >= 0;
 	}
 
 	@Override
 	protected OverlayPreferenceStore.OverlayKey[] createOverlayStoreKeys() {
-		ArrayList overlayKeys = new ArrayList();
+		ArrayList overlayKeys = new ArrayList<>();
 
 		overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, CEditor.SUB_WORD_NAVIGATION));
 		overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, PreferenceConstants.EDITOR_EVALUATE_TEMPORARY_PROBLEMS));
@@ -90,6 +103,10 @@ public class CEditorPreferencePage extends AbstractPreferencePage {
 		overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.STRING, ContentAssistPreference.PARAMETERS_FOREGROUND));
 		overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.STRING, PreferenceConstants.EDITOR_SOURCE_HOVER_BACKGROUND_COLOR));
 		overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, PreferenceConstants.EDITOR_SOURCE_HOVER_BACKGROUND_COLOR_SYSTEM_DEFAULT));
+		if (formattingScopeForEmptySelectionSupported) {
+			overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.STRING, FORMATTING_SCOPE_FOR_EMPTY_SELECTION));
+			overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, FORMATTING_CONFIRM_SCOPE_FOR_EMPTY_SELECTION));
+		}
 
         OverlayPreferenceStore.OverlayKey[] keys = new OverlayPreferenceStore.OverlayKey[overlayKeys.size()];
 		overlayKeys.toArray(keys);
@@ -100,22 +117,21 @@ public class CEditorPreferencePage extends AbstractPreferencePage {
 		store.setDefault(CEditor.SUB_WORD_NAVIGATION, true);
 
 		store.setDefault(CEditor.MATCHING_BRACKETS, true);
-		PreferenceConverter.setDefault(store, CEditor.MATCHING_BRACKETS_COLOR, new RGB(170,170,170));
+		PreferenceConverter.setDefault(store, CEditor.MATCHING_BRACKETS_COLOR, new RGB(170, 170, 170));
 
 		store.setDefault(CEditor.INACTIVE_CODE_ENABLE, true);
 		PreferenceConverter.setDefault(store, CEditor.INACTIVE_CODE_COLOR, new RGB(224, 224, 224));
 	}
 
-	/*
-	 * @see PreferencePage#createControl(Composite)
-	 */
 	@Override
 	public void createControl(Composite parent) {
 		super.createControl(parent);
 		PlatformUI.getWorkbench().getHelpSystem().setHelp(getControl(), ICHelpContextIds.C_EDITOR_PREF_PAGE);
 	}
 
-	// sets enabled flag for a control and all its sub-tree
+	/**
+	 * Sets enabled flag for a control and all its sub-tree.
+	 */
 	protected static void setEnabled(Control control, boolean enable) {
 		control.setEnabled(enable);
 		if (control instanceof Composite) {
@@ -133,6 +149,8 @@ public class CEditorPreferencePage extends AbstractPreferencePage {
 		layout.numColumns = 2;
 		behaviorComposite.setLayout(layout);
 
+		int indent = convertHorizontalDLUsToPixels(8);
+
 		String label= PreferencesMessages.CEditorPreferencePage_behaviorPage_subWordNavigation;
 		addCheckBox(behaviorComposite, label, CEditor.SUB_WORD_NAVIGATION, 0);
 
@@ -145,6 +163,23 @@ public class CEditorPreferencePage extends AbstractPreferencePage {
 		label = PreferencesMessages.CEditorPreferencePage_behaviorPage_inactiveCode;
 		addCheckBox(behaviorComposite, label, CEditor.INACTIVE_CODE_ENABLE, 0);
 
+		if (formattingScopeForEmptySelectionSupported) {
+			Label l = new Label(behaviorComposite, SWT.LEFT);
+			l.setText(PreferencesMessages.CEditorPreferencePage_behaviorPage_formattingWithEmptySelection);
+			GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+			gd.horizontalSpan = 2;
+			l.setLayoutData(gd);
+
+			label = PreferencesMessages.CEditorPreferencePage_behaviorPage_formatFile;
+			addRadioButton(behaviorComposite, label, FORMATTING_SCOPE_FOR_EMPTY_SELECTION, FORMATTING_SCOPE_DOCUMENT, indent);
+	
+			label = PreferencesMessages.CEditorPreferencePage_behaviorPage_formatStatement;
+			addRadioButton(behaviorComposite, label, FORMATTING_SCOPE_FOR_EMPTY_SELECTION, FORMATTING_SCOPE_STATEMENT, indent);
+	
+			label = PreferencesMessages.CEditorPreferencePage_behaviorPage_confirmFormattingScope;
+			addCheckBox(behaviorComposite, label, FORMATTING_CONFIRM_SCOPE_FOR_EMPTY_SELECTION, indent);
+		}
+
 		Label l = new Label(behaviorComposite, SWT.LEFT);
 		GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
 		gd.horizontalSpan = 2;
@@ -257,7 +292,7 @@ public class CEditorPreferencePage extends AbstractPreferencePage {
 		String text = PreferencesMessages.CEditorPreferencePage_link;
 		Link link = new Link(parent, SWT.NONE);
 		link.setText(text);
-		link.addListener (SWT.Selection, new Listener () {
+		link.addListener (SWT.Selection, new Listener() {
 			@Override
 			public void handleEvent(Event event) {
 				String u = event.text;
@@ -274,9 +309,6 @@ public class CEditorPreferencePage extends AbstractPreferencePage {
 		return link;
 	}
 
-	/*
-	 * @see PreferencePage#createContents(Composite)
-	 */
 	@Override
 	protected Control createContents(Composite parent) {
 		fOverlayStore.load();
@@ -319,9 +351,6 @@ public class CEditorPreferencePage extends AbstractPreferencePage {
 		});
 	}
 
-	/*
-	 * @see org.eclipse.cdt.internal.ui.preferences.AbstractPreferencePage#performOk()
-	 */
 	@Override
 	public boolean performOk() {
 		DocCommentOwnerManager.getInstance().setWorkspaceCommentOwner(fDocCommentOwnerComposite.getSelectedDocCommentOwner());
@@ -341,9 +370,6 @@ public class CEditorPreferencePage extends AbstractPreferencePage {
 		}
 	}
 
-	/*
-	 * @see org.eclipse.cdt.internal.ui.preferences.AbstractPreferencePage#performDefaults()
-	 */
 	@Override
 	protected void performDefaults() {
 		super.performDefaults();
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.java
index 074be583b9b..8b812b517b6 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.java
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.java
@@ -100,6 +100,10 @@ public final class PreferencesMessages extends NLS {
 	public static String CEditorPreferencePage_behaviorPage_matchingBrackets;
 	public static String CEditorPreferencePage_behaviorPage_subWordNavigation;
 	public static String CEditorPreferencePage_behaviorPage_inactiveCode;
+	public static String CEditorPreferencePage_behaviorPage_formattingWithEmptySelection;
+	public static String CEditorPreferencePage_behaviorPage_formatFile; 
+	public static String CEditorPreferencePage_behaviorPage_formatStatement;
+	public static String CEditorPreferencePage_behaviorPage_confirmFormattingScope;
 	public static String CEditorPreferencePage_behaviorPage_appearanceColorOptions;
 	public static String CEditorPreferencePage_behaviorPage_matchingBracketColor;
 	public static String CEditorPreferencePage_behaviorPage_inactiveCodeColor;
@@ -581,4 +585,4 @@ public final class PreferencesMessages extends NLS {
 	static {
 		NLS.initializeMessages(PreferencesMessages.class.getName(), PreferencesMessages.class);
 	}
-}
\ No newline at end of file
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties
index c3f4874407c..2b6c5c06e93 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties
@@ -103,6 +103,10 @@ CEditorPreferencePage_colorPage_systemDefault=S&ystem Default
 CEditorPreferencePage_behaviorPage_matchingBrackets=Highlight &matching brackets
 CEditorPreferencePage_behaviorPage_subWordNavigation=Smart &caret positioning in identifiers
 CEditorPreferencePage_behaviorPage_inactiveCode=Highlight &inactive code
+CEditorPreferencePage_behaviorPage_formattingWithEmptySelection=When formatting code with an empty selection:
+CEditorPreferencePage_behaviorPage_formatFile=Format the entire &file 
+CEditorPreferencePage_behaviorPage_formatStatement=Format the &statement on the current line
+CEditorPreferencePage_behaviorPage_confirmFormattingScope=&Ask for confirmation
 CEditorPreferencePage_behaviorPage_appearanceColorOptions=Appearance color options:
 CEditorPreferencePage_behaviorPage_matchingBracketColor=Matching brackets highlight
 CEditorPreferencePage_behaviorPage_inactiveCodeColor=Inactive code highlight
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/PreferenceConstants.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/PreferenceConstants.java
index c5f25813ecd..08d16e0f2e1 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/PreferenceConstants.java
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/PreferenceConstants.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2005, 2012 IBM Corporation and others.
+ * Copyright (c) 2005, 2014 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -930,6 +930,41 @@ public class PreferenceConstants {
 	 */
 	public static final String FORMATTER_PROFILE = "formatter_profile"; //$NON-NLS-1$
 
+	/**
+	 * Preference key determining the scope of formatting when the selection is empty.
+	 * 

+ * Value is of type {@code String}. + * + * @since 5.9 + */ + public final static String FORMATTING_SCOPE_FOR_EMPTY_SELECTION = "formattingScopeForEmptySelection"; //$NON-NLS-1$ + + /** + * The value of {@link #FORMATTING_SCOPE_FOR_EMPTY_SELECTION} specifying that the formatting + * applies to the whole document. + * + * @since 5.9 + */ + public static final String FORMATTING_SCOPE_DOCUMENT = "document"; //$NON-NLS-1$ + + /** + * The value of {@link #FORMATTING_SCOPE_FOR_EMPTY_SELECTION} specifying that the formatting + * applies to the containing statement. + * + * @since 5.9 + */ + public static final String FORMATTING_SCOPE_STATEMENT = "statement"; //$NON-NLS-1$ + + /** + * Preference key for whether to ask user for how formatting of an empty selection + * should be interpreted. + *

+ * Value is of type {@code Boolean}. The default is {@code true}. + * + * @since 5.9 + */ + public final static String FORMATTING_CONFIRM_SCOPE_FOR_EMPTY_SELECTION = "formattingConfirmScopeForEmptySelection"; //$NON-NLS-1$ + /** * Preference key for whether to ensure a newline at the end of files when saving. * @@ -2159,6 +2194,10 @@ public class PreferenceConstants { // Formatter profile store.setDefault(FORMATTER_PROFILE, FormatterProfileManager.DEFAULT_PROFILE); + // Formatting behavior. + store.setDefault(FORMATTING_SCOPE_FOR_EMPTY_SELECTION, FORMATTING_SCOPE_DOCUMENT); + store.setDefault(FORMATTING_CONFIRM_SCOPE_FOR_EMPTY_SELECTION, true); + // Content assist store.setDefault(CODEASSIST_EXCLUDED_CATEGORIES, "org.eclipse.cdt.ui.textProposalCategory\0"); //$NON-NLS-1$ store.setDefault(CODEASSIST_CATEGORY_ORDER, "org.eclipse.cdt.ui.parserProposalCategory:65539\0org.eclipse.cdt.ui.textProposalCategory:65541\0org.eclipse.cdt.ui.templateProposalCategory:2\0org.eclipse.cdt.ui.helpProposalCategory:5\0"); //$NON-NLS-1$