Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarco Stornelli2019-02-16 10:02:42 +0000
committerJonah Graham2019-02-19 20:49:58 +0000
commita6d06902b1f2626f3fadbeeb55a52979af22262a (patch)
tree3b185e5159fb1d2a198e95a6a72628608906b5d9
parentf60bbf25dda0d99fe7c7cd781afae6f3455a0908 (diff)
downloadorg.eclipse.cdt-a6d06902b1f2626f3fadbeeb55a52979af22262a.tar.gz
org.eclipse.cdt-a6d06902b1f2626f3fadbeeb55a52979af22262a.tar.xz
org.eclipse.cdt-a6d06902b1f2626f3fadbeeb55a52979af22262a.zip
Bug 496249: Tags for disabling/enabling CDT code formatter
Change-Id: I4389c61612da6a4ee0011a49d6aeed7b52152436 Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
-rw-r--r--core/org.eclipse.cdt.core/META-INF/MANIFEST.MF2
-rw-r--r--core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/DefaultCodeFormatterConstants.java47
-rw-r--r--core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/DefaultCodeFormatterOptions.java26
-rw-r--r--core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CodeFormatterVisitor.java62
-rw-r--r--core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/Scribe.java40
-rw-r--r--core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/CodeFormatterTest.java40
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterMessages.java10
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterMessages.properties10
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterModifyDialog.java1
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterTagTabPage.java93
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/ModifyDialogTabPage.java160
11 files changed, 484 insertions, 7 deletions
diff --git a/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF b/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF
index 3d146697542..84147ef8242 100644
--- a/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF
+++ b/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.cdt.core; singleton:=true
-Bundle-Version: 6.6.100.qualifier
+Bundle-Version: 6.7.0.qualifier
Bundle-Activator: org.eclipse.cdt.core.CCorePlugin
Bundle-Vendor: %providerName
Bundle-Localization: plugin
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 9b0f72a7a95..d4bec0effd5 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
@@ -91,6 +91,38 @@ public class DefaultCodeFormatterConstants {
*/
public static final String FALSE = "false"; //$NON-NLS-1$
+ /**
+ * <pre>
+ * FORMATTER / Formatter on tag format option
+ * - option id: "org.eclipse.cdt.core.formatter.comment_formatter_on_tag"
+ * - default: @formatter:on
+ * </pre>
+ * @see CCorePlugin#FORMAT_ON_TAG
+ * @since 6.7
+ */
+ public static final String FORMATTER_COMMENT_ON_TAG = CCorePlugin.PLUGIN_ID + ".formatter.comment_formatter_on_tag"; //$NON-NLS-1$
+ /**
+ * <pre>
+ * FORMATTER / Formatter off tag format option
+ * - option id: "org.eclipse.cdt.core.formatter.comment_formatter_off_tag"
+ * - default: @formatter:off
+ * </pre>
+ * @see CCorePlugin#FORMAT_OFF_TAG
+ * @since 6.7
+ */
+ public static final String FORMATTER_COMMENT_OFF_TAG = CCorePlugin.PLUGIN_ID
+ + ".formatter.comment_formatter_off_tag"; //$NON-NLS-1$
+ /**
+ * <pre>
+ * FORMATTER / Formatter on tag format option
+ * - option id: "org.eclipse.cdt.core.formatter.use_comment_formatter_tag"
+ * - default: true
+ * </pre>
+ * @since 6.7
+ */
+ public static final String FORMATTER_USE_COMMENT_TAG = CCorePlugin.PLUGIN_ID
+ + ".formatter.use_comment_formatter_tag"; //$NON-NLS-1$
+
// /**
// * <pre>
// * FORMATTER / Option to align type members of a type declaration on column
@@ -2484,6 +2516,21 @@ public class DefaultCodeFormatterConstants {
*/
public static final int WRAP_ONE_PER_LINE = 3;
+ /**
+ * <pre>
+ * FORMATTER / Default formatter on tag
+ * </pre>
+ * @since 6.7
+ */
+ public static final String FORMATTER_ON_TAG = "@formatter:on"; //$NON-NLS-1$
+ /**
+ * <pre>
+ * FORMATTER / Default formatter off tag
+ * </pre>
+ * @since 6.7
+ */
+ public static final String FORMATTER_OFF_TAG = "@formatter:off"; //$NON-NLS-1$
+
/*
* Private constants.
*/
diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/DefaultCodeFormatterOptions.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/DefaultCodeFormatterOptions.java
index 6aac8f17204..65d4101d39a 100644
--- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/DefaultCodeFormatterOptions.java
+++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/DefaultCodeFormatterOptions.java
@@ -264,6 +264,12 @@ public class DefaultCodeFormatterOptions {
public boolean use_tabs_only_for_leading_indentations;
public int initial_indentation_level;
public String line_separator;
+ /** @since 6.7 */
+ public String comment_formatter_on_tag;
+ /** @since 6.7 */
+ public String comment_formatter_off_tag;
+ /** @since 6.7 */
+ public boolean use_fomatter_comment_tag;
private DefaultCodeFormatterOptions() {
// cannot be instantiated
@@ -282,6 +288,11 @@ public class DefaultCodeFormatterOptions {
public Map<String, String> getMap() {
Map<String, String> options = new HashMap<>();
+ options.put(DefaultCodeFormatterConstants.FORMATTER_COMMENT_ON_TAG, comment_formatter_on_tag);
+ options.put(DefaultCodeFormatterConstants.FORMATTER_COMMENT_OFF_TAG, comment_formatter_off_tag);
+ options.put(DefaultCodeFormatterConstants.FORMATTER_USE_COMMENT_TAG,
+ this.use_fomatter_comment_tag ? DefaultCodeFormatterConstants.TRUE
+ : DefaultCodeFormatterConstants.FALSE);
// options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_ARGUMENTS_IN_ALLOCATION_EXPRESSION, getAlignment(this.alignment_for_arguments_in_allocation_expression));
options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_ARGUMENTS_IN_METHOD_INVOCATION,
getAlignment(this.alignment_for_arguments_in_method_invocation));
@@ -2025,10 +2036,25 @@ public class DefaultCodeFormatterOptions {
this.tab_char = MIXED;
}
}
+ final Object formatterCommentOnTag = settings.get(DefaultCodeFormatterConstants.FORMATTER_COMMENT_ON_TAG);
+ if (formatterCommentOnTag != null) {
+ this.comment_formatter_on_tag = (String) formatterCommentOnTag;
+ }
+ final Object formatterCommentOffTag = settings.get(DefaultCodeFormatterConstants.FORMATTER_COMMENT_OFF_TAG);
+ if (formatterCommentOffTag != null) {
+ this.comment_formatter_off_tag = (String) formatterCommentOffTag;
+ }
+ final Object useFormatterCommentTag = settings.get(DefaultCodeFormatterConstants.FORMATTER_USE_COMMENT_TAG);
+ if (useFormatterCommentTag != null) {
+ this.use_fomatter_comment_tag = DefaultCodeFormatterConstants.TRUE.equals(useFormatterCommentTag);
+ }
}
public void setDefaultSettings() {
// this.alignment_for_arguments_in_allocation_expression = Alignment.M_COMPACT_SPLIT;
+ this.comment_formatter_on_tag = DefaultCodeFormatterConstants.FORMATTER_ON_TAG;
+ this.comment_formatter_off_tag = DefaultCodeFormatterConstants.FORMATTER_OFF_TAG;
+ this.use_fomatter_comment_tag = true;
this.alignment_for_arguments_in_method_invocation = Alignment.M_COMPACT_SPLIT;
this.alignment_for_assignment = Alignment.M_COMPACT_SPLIT;
this.alignment_for_base_clause_in_type_declaration = Alignment.M_NEXT_PER_LINE_SPLIT;
diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CodeFormatterVisitor.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CodeFormatterVisitor.java
index 22ae42e2b46..4ed22399500 100644
--- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CodeFormatterVisitor.java
+++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CodeFormatterVisitor.java
@@ -35,6 +35,7 @@ import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression;
import org.eclipse.cdt.core.dom.ast.IASTBreakStatement;
import org.eclipse.cdt.core.dom.ast.IASTCaseStatement;
import org.eclipse.cdt.core.dom.ast.IASTCastExpression;
+import org.eclipse.cdt.core.dom.ast.IASTComment;
import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTCompoundStatement;
import org.eclipse.cdt.core.dom.ast.IASTConditionalExpression;
@@ -438,7 +439,8 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
localScanner.setSource(compilationUnitSource);
scribe.initializeScanner(compilationUnitSource);
- scribe.setSkipPositions(collectInactiveCodePositions(unit));
+ scribe.setSkipInactivePositions(collectInactiveCodePositions(unit));
+ scribe.setSkipForbiddenPositions(collectNoFormatCodePositions(unit));
fStatus = new MultiStatus(CCorePlugin.PLUGIN_ID, 0, "Formatting problem(s) in '" + unit.getFilePath() + "'", //$NON-NLS-1$//$NON-NLS-2$
null);
@@ -4466,6 +4468,64 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
}
/**
+ * Collect source positions of no-format sections in the given translation unit.
+ *
+ * @param translationUnit the {@link IASTTranslationUnit}, may be <code>null</code>
+ * @return a {@link List} of {@link Position}s
+ */
+ private List<Position> collectNoFormatCodePositions(IASTTranslationUnit translationUnit) {
+ if (translationUnit == null || !this.preferences.use_fomatter_comment_tag) {
+ return Collections.emptyList();
+ }
+ String fileName = translationUnit.getFilePath();
+ if (fileName == null) {
+ return Collections.emptyList();
+ }
+ List<Position> positions = new ArrayList<>();
+ int inactiveCodeStart = -1;
+ boolean inInactiveCode = false;
+
+ IASTComment[] commentsStmts = translationUnit.getComments();
+
+ for (IASTComment commentStmt : commentsStmts) {
+ IASTComment statement = commentStmt;
+ if (!statement.isPartOfTranslationUnitFile()) {
+ // comment is from a different file
+ continue;
+ }
+ IASTNodeLocation nodeLocation = statement.getFileLocation();
+ if (nodeLocation == null) {
+ continue;
+ }
+
+ String comment = new String(statement.getComment());
+ /**
+ * According to JDT formatter rules, we need to evaluate the latest tag if both
+ * are defined at the same time in the comment.
+ */
+ int offPos = comment.lastIndexOf(this.preferences.comment_formatter_off_tag);
+ int onPos = comment.lastIndexOf(this.preferences.comment_formatter_on_tag);
+ if (offPos != -1 && offPos > onPos) {
+ if (!inInactiveCode) {
+ inactiveCodeStart = nodeLocation.getNodeOffset();
+ inInactiveCode = true;
+ }
+ } else if (onPos != -1 && onPos > offPos) {
+ if (inInactiveCode) {
+ int inactiveCodeEnd = nodeLocation.getNodeOffset();
+ positions.add(new Position(inactiveCodeStart, inactiveCodeEnd - inactiveCodeStart));
+ }
+ inInactiveCode = false;
+ }
+ }
+ if (inInactiveCode) {
+ positions.add(new Position(inactiveCodeStart, translationUnit.getFileLocation().getNodeLength()));
+ inInactiveCode = false;
+ }
+ return positions;
+ }
+
+ /**
* Collect source positions of preprocessor-hidden branches
* in the given translation unit.
*
diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/Scribe.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/Scribe.java
index 2b34203f45b..4bdc4a57ebb 100644
--- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/Scribe.java
+++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/Scribe.java
@@ -88,7 +88,14 @@ public class Scribe {
private int textRegionStart;
public int scannerEndPosition;
- private List<Position> fSkipPositions = Collections.emptyList();
+ /**
+ * It keeps the list of inactive region.
+ */
+ private List<Position> fSkipInactivePositions = Collections.emptyList();
+ /**
+ * It keeps the list of no-format region.
+ */
+ private List<Position> fSkipForbiddenPositions = Collections.emptyList();
private boolean skipOverInactive;
@@ -668,10 +675,19 @@ public class Scribe {
}
/**
+ * Set the positions where we don't want to perform any check
+ * @param list The list of positions
+ */
+ public void setSkipForbiddenPositions(List<Position> list) {
+ if (list != null)
+ fSkipForbiddenPositions = list;
+ }
+
+ /**
* @param list
*/
- public void setSkipPositions(List<Position> list) {
- fSkipPositions = list;
+ public void setSkipInactivePositions(List<Position> list) {
+ fSkipInactivePositions = list;
skipOverInactive = !list.isEmpty();
}
@@ -1287,8 +1303,22 @@ public class Scribe {
* @param offset
* @return
*/
+ private boolean isForbidden(int offset) {
+ for (Iterator<Position> iter = fSkipForbiddenPositions.iterator(); iter.hasNext();) {
+ Position pos = iter.next();
+ if (pos.includes(offset)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @param offset
+ * @return
+ */
private Position getInactivePosAt(int offset) {
- for (Iterator<Position> iter = fSkipPositions.iterator(); iter.hasNext();) {
+ for (Iterator<Position> iter = fSkipInactivePositions.iterator(); iter.hasNext();) {
Position pos = iter.next();
if (pos.includes(offset)) {
return pos;
@@ -2037,7 +2067,7 @@ public class Scribe {
}
boolean shouldSkip(int offset) {
- return offset >= fSkipStartOffset && offset < fSkipEndOffset;
+ return ((offset >= fSkipStartOffset && offset < fSkipEndOffset) || isForbidden(offset));
}
void skipRange(int offset, int endOffset) {
diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/CodeFormatterTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/CodeFormatterTest.java
index 4c2a10cc35a..f959926efae 100644
--- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/CodeFormatterTest.java
+++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/CodeFormatterTest.java
@@ -3563,4 +3563,44 @@ public class CodeFormatterTest extends BaseUITestCase {
public void testAttributedNamedScopedEnumDeclaration_Bug535256_4() throws Exception {
assertFormatterResult();
}
+
+ ////@formatter:off
+ //int
+ //main(){
+ //return
+ //0
+ //;}
+ ////@formatter:on
+
+ ////@formatter:off
+ //int
+ //main(){
+ //return
+ //0
+ //;}
+ ////@formatter:on
+ public void testOnOffTags() throws Exception {
+ fOptions.put(DefaultCodeFormatterConstants.FORMATTER_USE_COMMENT_TAG, true);
+ fOptions.put(DefaultCodeFormatterConstants.FORMATTER_COMMENT_ON_TAG, "@formatter:on");
+ fOptions.put(DefaultCodeFormatterConstants.FORMATTER_COMMENT_OFF_TAG, "@formatter:off");
+ assertFormatterResult();
+ }
+
+ ////@formatter:off
+ // void this_line_intentionally_indented() {
+ // int x;
+ // }
+ ////@formatter:on
+
+ ////@formatter:off
+ // void this_line_intentionally_indented() {
+ // int x;
+ // }
+ ////@formatter:on
+ public void testOnOffTagsDoesNotChangeFirstLineIndent() throws Exception {
+ fOptions.put(DefaultCodeFormatterConstants.FORMATTER_USE_COMMENT_TAG, true);
+ fOptions.put(DefaultCodeFormatterConstants.FORMATTER_COMMENT_ON_TAG, "@formatter:on");
+ fOptions.put(DefaultCodeFormatterConstants.FORMATTER_COMMENT_OFF_TAG, "@formatter:off");
+ assertFormatterResult();
+ }
}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterMessages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterMessages.java
index 6deaa290f1b..39eb4f6a6c8 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterMessages.java
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterMessages.java
@@ -369,6 +369,7 @@ final class FormatterMessages extends NLS {
public static String ModifyDialog_tabpage_braces_title;
public static String ModifyDialog_tabpage_indentation_title;
public static String ModifyDialog_tabpage_whitespace_title;
+ public static String ModifyDialog_tabpage_formatter_tag_title;
// public static String ModifyDialog_tabpage_blank_lines_title;
public static String ModifyDialog_tabpage_new_lines_title;
public static String ModifyDialog_tabpage_control_statements_title;
@@ -402,6 +403,15 @@ final class FormatterMessages extends NLS {
public static String CPreview_formatter_exception;
+ public static String FormatterModifyDialog_offOn_preview_header;
+ public static String FormatterModifyDialog_offOn_description;
+ public static String FormatterModifyDialog_offOn_pref_enable;
+ public static String FormatterModifyDialog_offOn_pref_off_tag;
+ public static String FormatterModifyDialog_offOn_pref_on_tag;
+ public static String FormatterModifyDialog_offOn_error_startsWithWhitespace;
+ public static String FormatterModifyDialog_offOn_error_endsWithWhitespace;
+ public static String FormatterModifyDialog_offOn_error_empty;
+
private FormatterMessages() {
// Do not instantiate
}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterMessages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterMessages.properties
index e03b4ad3646..c879b3ae186 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterMessages.properties
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterMessages.properties
@@ -433,6 +433,7 @@ ModifyDialog_tabpage_new_lines_title=New &Lines
ModifyDialog_tabpage_control_statements_title=Con&trol Statements
ModifyDialog_tabpage_line_wrapping_title=Line Wra&pping
ModifyDialog_tabpage_comments_title=Co&mments
+ModifyDialog_tabpage_formatter_tag_title=Off/On Tags
ModifyDialog_dialog_title=Profile ''{0}''
ModifyDialog_apply_button=Apply
@@ -476,3 +477,12 @@ CPreview_formatter_exception=The formatter threw an unhandled exception while fo
ProfileConfigurationBlock_load_profile_wrong_profile_message=Import failed. This is not a valid profile: Expected ''{0}'' but encountered ''{1}''.
FormatterTabPage_ShowInvisibleCharacters_label=Show &invisible characters
+
+FormatterModifyDialog_offOn_preview_header=Off/On tags
+FormatterModifyDialog_offOn_description=Off/On tags can be used in any comments to turn the formatter off and on in a source file.\n- At the beginning of each file, the formatter is enabled.\n- Each time the formatter sees an off tag, it disables formatting for that comment and the source after it.\n- Each time the formatter sees an on tag, it enables formatting for the source after that comment.\n
+FormatterModifyDialog_offOn_pref_enable=Enable Off/On tags
+FormatterModifyDialog_offOn_pref_off_tag=Off tag:
+FormatterModifyDialog_offOn_pref_on_tag=On tag:
+FormatterModifyDialog_offOn_error_startsWithWhitespace=This value must not start with a white space.
+FormatterModifyDialog_offOn_error_endsWithWhitespace=This value must not end with a white space.
+FormatterModifyDialog_offOn_error_empty=This value must not be empty.
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterModifyDialog.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterModifyDialog.java
index 5838b0bc7b9..e0b242a5260 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterModifyDialog.java
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterModifyDialog.java
@@ -38,5 +38,6 @@ public class FormatterModifyDialog extends ModifyDialog {
new ControlStatementsTabPage(this, values));
addTabPage(FormatterMessages.ModifyDialog_tabpage_line_wrapping_title, new LineWrappingTabPage(this, values));
addTabPage(FormatterMessages.ModifyDialog_tabpage_comments_title, new CommentsTabPage(this, values));
+ addTabPage(FormatterMessages.ModifyDialog_tabpage_formatter_tag_title, new FormatterTagTabPage(this, values));
}
}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterTagTabPage.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterTagTabPage.java
new file mode 100644
index 00000000000..4b4f39eb152
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterTagTabPage.java
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Marco Stornelli
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ *******************************************************************************/
+package org.eclipse.cdt.internal.ui.preferences.formatter;
+
+import java.util.Map;
+import java.util.Observable;
+import java.util.Observer;
+
+import org.eclipse.cdt.core.formatter.DefaultCodeFormatterConstants;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
+
+public class FormatterTagTabPage extends FormatterTabPage {
+
+ private final String PREVIEW = createPreviewHeader(FormatterMessages.FormatterModifyDialog_offOn_preview_header)
+ + "void method1() { doSomething(); }\n\n// @formatter:off\n" //$NON-NLS-1$
+ + "void method2() { doSomething(); }\n// @formatter:on\n\n" //$NON-NLS-1$
+ + "void method3() { doSomething(); }\n\n" //$NON-NLS-1$
+ + "/* @formatter:off */\n\nvoid\nfoo()\n;"; //$NON-NLS-1$
+
+ private StringPreference fOnTag;
+ private StringPreference fOffTag;
+ private CheckboxPreference fUseTag;
+ private TranslationUnitPreview fPreview;
+
+ public FormatterTagTabPage(IModificationListener modifyListener, Map<String, String> workingValues) {
+ super(modifyListener, workingValues);
+ }
+
+ @Override
+ protected void initializePage() {
+ fPreview.setPreviewText(PREVIEW);
+ }
+
+ @Override
+ protected void doUpdatePreview() {
+ super.doUpdatePreview();
+ fPreview.update();
+ }
+
+ @Override
+ protected void doCreatePreferences(Composite composite, int numColumns) {
+ final Group generalGroup = createGroup(numColumns, composite,
+ FormatterMessages.ModifyDialog_tabpage_formatter_tag_title);
+ createLabel(numColumns, generalGroup, FormatterMessages.FormatterModifyDialog_offOn_description);
+ fUseTag = createCheckboxPref(generalGroup, numColumns,
+ FormatterMessages.FormatterModifyDialog_offOn_pref_enable,
+ DefaultCodeFormatterConstants.FORMATTER_USE_COMMENT_TAG, FALSE_TRUE);
+ fUseTag.addObserver(new Observer() {
+ @Override
+ public void update(Observable arg0, Object arg1) {
+ fOnTag.setEnabled(fUseTag.getChecked());
+ fOffTag.setEnabled(fUseTag.getChecked());
+ }
+ });
+ PreferenceValidator validator = new PreferenceValidator() {
+ @Override
+ public String validate(String value) {
+ if (value != null && !value.isEmpty()) {
+ if (Character.isWhitespace(value.charAt(0))) {
+ return FormatterMessages.FormatterModifyDialog_offOn_error_startsWithWhitespace;
+ } else if (Character.isWhitespace(value.charAt(value.length() - 1))) {
+ return FormatterMessages.FormatterModifyDialog_offOn_error_endsWithWhitespace;
+ }
+ return null;
+ }
+ return FormatterMessages.FormatterModifyDialog_offOn_error_empty;
+ }
+ };
+ fOffTag = createStringPref(generalGroup, numColumns, FormatterMessages.FormatterModifyDialog_offOn_pref_off_tag,
+ DefaultCodeFormatterConstants.FORMATTER_COMMENT_OFF_TAG);
+ fOffTag.setValidator(validator);
+ fOnTag = createStringPref(generalGroup, numColumns, FormatterMessages.FormatterModifyDialog_offOn_pref_on_tag,
+ DefaultCodeFormatterConstants.FORMATTER_COMMENT_ON_TAG);
+ fOnTag.setValidator(validator);
+ }
+
+ @Override
+ protected CPreview doCreateCPreview(Composite parent) {
+ fPreview = new TranslationUnitPreview(fWorkingValues, parent);
+ return fPreview;
+ }
+
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/ModifyDialogTabPage.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/ModifyDialogTabPage.java
index 4401c3080c0..65101096722 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/ModifyDialogTabPage.java
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/ModifyDialogTabPage.java
@@ -69,6 +69,18 @@ public abstract class ModifyDialogTabPage implements IModifyDialogTabPage {
};
/**
+ * Preference validator
+ */
+ interface PreferenceValidator {
+ /**
+ * Validate callback
+ * @param value The value to be checked
+ * @return String error or null otherwise
+ */
+ String validate(String value);
+ }
+
+ /**
* The base class of all Preference classes. A preference class provides a wrapper
* around one or more SWT widgets and handles the input of values for some key.
* On each change, the new value is written to the map and the listeners are notified.
@@ -77,6 +89,7 @@ public abstract class ModifyDialogTabPage implements IModifyDialogTabPage {
private final Map<String, String> fPreferences;
private boolean fEnabled;
private String fKey;
+ private PreferenceValidator fValidator;
/**
* Create a new Preference.
@@ -143,6 +156,22 @@ public abstract class ModifyDialogTabPage implements IModifyDialogTabPage {
* of this object has changed (enabled, key, ...).
*/
protected abstract void updateWidget();
+
+ public void setValidator(PreferenceValidator validator) {
+ fValidator = validator;
+ }
+
+ /**
+ * Check if preference is valid according to its validator
+ * @param value The preference value
+ * @return Null if valid, the error string otherwise
+ */
+ protected String isValid(String value) {
+ if (fValidator != null) {
+ return fValidator.validate(value);
+ }
+ return null;
+ }
}
/**
@@ -319,6 +348,126 @@ public abstract class ModifyDialogTabPage implements IModifyDialogTabPage {
}
/**
+ * Wrapper around a textfied
+ */
+ protected final class StringPreference extends Preference {
+
+ private final Label fLabel;
+ private final Text fText;
+
+ protected String fSelected;
+ protected String fOldSelected;
+
+ /**
+ * Create a new NumberPreference.
+ * @param composite The composite on which the SWT widgets are added.
+ * @param numColumns The number of columns in the composite's GridLayout.
+ * @param preferences The map to store the values.
+ * @param key The key to store the values.
+ * @param text The label text for this Preference.
+ */
+ public StringPreference(Composite composite, int numColumns, Map<String, String> preferences, String key,
+ String text) {
+ super(preferences, key);
+
+ fLabel = createLabel(numColumns - 1, composite, text, GridData.FILL_HORIZONTAL);
+ fText = new Text(composite, SWT.SINGLE | SWT.BORDER | SWT.LEFT);
+ fText.setFont(composite.getFont());
+
+ fText.setLayoutData(
+ createGridData(1, GridData.HORIZONTAL_ALIGN_END, fPixelConverter.convertWidthInCharsToPixels(20)));
+
+ updateWidget();
+
+ fText.addFocusListener(new FocusListener() {
+ @Override
+ public void focusGained(FocusEvent e) {
+ StringPreference.this.focusGained();
+ }
+
+ @Override
+ public void focusLost(FocusEvent e) {
+ StringPreference.this.focusLost();
+ }
+ });
+
+ fText.addModifyListener(new ModifyListener() {
+ @Override
+ public void modifyText(ModifyEvent e) {
+ fieldModified();
+ }
+ });
+ }
+
+ private IStatus createErrorStatus(String error) {
+ return new Status(IStatus.ERROR, CUIPlugin.getPluginId(), 0, Messages.format(error), null);
+ }
+
+ protected void focusGained() {
+ fOldSelected = fSelected;
+ fText.setSelection(0, fText.getCharCount());
+ }
+
+ protected void focusLost() {
+ updateStatus(null);
+ final String input = fText.getText();
+ if (validInput(input) != null)
+ fSelected = fOldSelected;
+ else
+ fSelected = input;
+ if (fSelected != fOldSelected) {
+ saveSelected();
+ fText.setText(fSelected);
+ }
+ }
+
+ protected void fieldModified() {
+ final String trimInput = fText.getText().trim();
+ final String error = validInput(fText.getText());
+
+ updateStatus(error == null ? null : createErrorStatus(error));
+
+ if (error == null) {
+ if (fSelected.equals(trimInput)) {
+ fSelected = trimInput;
+ saveSelected();
+ }
+ }
+ }
+
+ private String validInput(String input) {
+ return isValid(input);
+ }
+
+ private void saveSelected() {
+ getPreferences().put(getKey(), fSelected);
+ setChanged();
+ notifyObservers();
+ }
+
+ @Override
+ protected void updateWidget() {
+ final boolean hasKey = getKey() != null;
+
+ fLabel.setEnabled(hasKey && getEnabled());
+ fText.setEnabled(hasKey && getEnabled());
+
+ if (hasKey) {
+ String s = getPreferences().get(getKey());
+ fSelected = s;
+ fText.setText(s);
+ } else {
+ fText.setText(""); //$NON-NLS-1$
+ }
+ }
+
+ @Override
+ public Control getControl() {
+ return fText;
+ }
+ }
+
+ /**
* Wrapper around a textfied which requests an integer input of a given range.
*/
protected final class NumberPreference extends Preference {
@@ -901,6 +1050,17 @@ public abstract class ModifyDialogTabPage implements IModifyDialogTabPage {
* Convenience method to create a NumberPreference. The widget is registered as
* a potential focus holder, and the default updater is added.
*/
+ protected StringPreference createStringPref(Composite composite, int numColumns, String name, String key) {
+ final StringPreference pref = new StringPreference(composite, numColumns, fWorkingValues, key, name);
+ fDefaultFocusManager.add(pref);
+ pref.addObserver(fUpdater);
+ return pref;
+ }
+
+ /*
+ * Convenience method to create a NumberPreference. The widget is registered as
+ * a potential focus holder, and the default updater is added.
+ */
protected NumberPreference createNumberPref(Composite composite, int numColumns, String name, String key,
int minValue, int maxValue) {
final NumberPreference pref = new NumberPreference(composite, numColumns, fWorkingValues, key, minValue,

Back to the top