1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-29 19:45:01 +02:00

Bug 425711: User preference to limit tokens per translation unit

This adds a new scalability preference that aborts parsing when too many
Tokens are created for a single translation unit.  This is a heuristic
that fairly close predicts the files that will be too complex for the
indexer to handle.

The token counter is disabled by default.

When it is enabled, the default token limit is 25,000,000.  This value
was determined by counting the number of Tokens produced for each
translation unit in the boost-1.55.0 sources:

sqlite> select * from counts where count > 10000000;
count       location
-----------------------------------------------------------------
100000001   libs/local_function/test/all_decl.cpp
100000001   libs/local_function/test/all_decl_seq.cpp
100000001   libs/local_function/test/all_decl_seq_nova.cpp
100000001   libs/preprocessor/doc/examples/array_arithmetic.c
99808587    libs/function_types/build/preprocess_arity_loops.cpp
62380381    libs/preprocessor/doc/examples/delay.c
58096841    libs/serialization/performance/xml/string256_test.cpp
58096828    libs/serialization/performance/xml/int256_test.cpp
52898416    libs/mpi/src/python/collectives.cpp
52573708    boost/spirit/home/support/char_encoding/ \
                  unicode/create_tables.cpp
21315014    libs/utility/binary_test.cpp
18799536    libs/math/test/test_rational_instances/ \
                 test_rational_double1.cpp
17758615    libs/mpl/test/string.cpp
13100401    libs/container/bench/bench_set.cpp
11976021    libs/local_function/example/const_block.cpp
11381198    libs/math/test/test_tr1.cpp
10432186    libs/parameter/test/preprocessor.cpp

This value means that the indexer will process all files in boost
without running out of memory on a 1Gb heap.

Change-Id: Ia9fc73dfb38454cc8735f537e3ac6e661864fb4f
Signed-off-by: Andrew Eidsness <eclipse@jfront.com>
Reviewed-on: https://git.eclipse.org/r/22386
Reviewed-by: Doug Schaefer <dschaefer@qnx.com>
IP-Clean: Doug Schaefer <dschaefer@qnx.com>
Tested-by: Doug Schaefer <dschaefer@qnx.com>
This commit is contained in:
Andrew Eidsness 2014-02-21 14:14:46 -05:00 committed by Doug Schaefer
parent 4aa3f06bb4
commit f7d49e5bde
16 changed files with 501 additions and 63 deletions

View file

@ -22,19 +22,15 @@ import java.util.Map.Entry;
import java.util.Vector;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.CCorePreferenceConstants;
import org.eclipse.cdt.core.cdtvariables.CdtVariableException;
import org.eclipse.cdt.core.cdtvariables.ICdtVariableManager;
import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsChangeEvent;
import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsChangeListener;
import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsManager;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.parser.ExtendedScannerInfo;
import org.eclipse.cdt.core.parser.IParserSettings;
import org.eclipse.cdt.core.parser.IScannerInfo;
import org.eclipse.cdt.core.parser.IScannerInfoChangeListener;
import org.eclipse.cdt.core.parser.IScannerInfoProvider;
import org.eclipse.cdt.core.parser.ParserSettings;
import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry;
import org.eclipse.cdt.core.settings.model.ICMacroEntry;
@ -42,7 +38,7 @@ import org.eclipse.cdt.core.settings.model.ICPathEntry;
import org.eclipse.cdt.core.settings.model.ICProjectDescription;
import org.eclipse.cdt.core.settings.model.ICSettingEntry;
import org.eclipse.cdt.core.settings.model.util.CDataUtil;
import org.eclipse.cdt.internal.core.model.CModelManager;
import org.eclipse.cdt.internal.core.parser.ParserSettings2;
import org.eclipse.cdt.internal.core.settings.model.CProjectDescriptionManager;
import org.eclipse.cdt.internal.core.settings.model.SettingsModelMessages;
import org.eclipse.cdt.utils.EFSExtensionManager;
@ -134,25 +130,10 @@ public class LanguageSettingsScannerInfoProvider implements IScannerInfoProvider
}
ExtendedScannerInfo extendedScannerInfo = new ExtendedScannerInfo(definedMacros, includePaths, macroFiles, includeFiles, includePathsLocal);
IParserSettings parserSettings = createParserSettings(project);
extendedScannerInfo.setParserSettings(parserSettings);
extendedScannerInfo.setParserSettings(new ParserSettings2(project));
return extendedScannerInfo;
}
private IParserSettings createParserSettings(IProject project) {
ParserSettings parserSettings = new ParserSettings();
ICProject cProject = CModelManager.getDefault().create(project);
if (CCorePreferenceConstants.getPreference(CCorePreferenceConstants.SCALABILITY_SKIP_TRIVIAL_EXPRESSIONS, cProject,
CCorePreferenceConstants.DEFAULT_SCALABILITY_SKIP_TRIVIAL_EXPRESSIONS)) {
int maximumNumberOfTrivialExpressionsInAggregateInitializers = CCorePreferenceConstants.getPreference(
CCorePreferenceConstants.SCALABILITY_MAXIMUM_TRIVIAL_EXPRESSIONS, cProject,
CCorePreferenceConstants.DEFAULT_SCALABILITY_MAXIMUM_TRIVIAL_EXPRESSIONS);
parserSettings.setMaximumTrivialExpressionsInAggregateInitializers(maximumNumberOfTrivialExpressionsInAggregateInitializers);
}
return parserSettings;
}
private String expandVariables(String pathStr, ICConfigurationDescription cfgDescription) {
try {
ICdtVariableManager varManager = CCorePlugin.getDefault().getCdtVariableManager();

View file

@ -35,6 +35,8 @@ import org.eclipse.cdt.core.parser.IParserSettings;
import org.eclipse.cdt.core.parser.IScanner;
import org.eclipse.cdt.core.parser.IScannerInfo;
import org.eclipse.cdt.core.parser.IncludeFileContentProvider;
import org.eclipse.cdt.core.parser.ParseError;
import org.eclipse.cdt.core.parser.ParseError.ParseErrorKind;
import org.eclipse.cdt.core.parser.ParserLanguage;
import org.eclipse.cdt.core.parser.ParserMode;
import org.eclipse.cdt.internal.core.parser.scanner.CPreprocessor;
@ -165,6 +167,20 @@ public abstract class AbstractCLikeLanguage extends AbstractLanguage implements
IASTTranslationUnit ast= parser.parse();
ast.setIsHeaderUnit((options & OPTION_IS_SOURCE_UNIT) == 0);
return ast;
} catch(ParseError e) {
// Only the TOO_MANY_TOKENS error can be handled here.
if (e.getErrorKind() != ParseErrorKind.TOO_MANY_TOKENS)
throw e;
// Otherwise generate a log because parsing was stopped because of a user preference.
if (log != null) {
String tuName = null;
if (scanner.getLocationResolver() != null)
tuName = scanner.getLocationResolver().getTranslationUnitPath();
log.traceLog(e.getMessage() + (tuName == null ? new String() : (" while parsing " + tuName))); //$NON-NLS-1$
}
return null;
} finally {
if (canceler != null) {
canceler.setCancelable(null);

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 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
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.cdt.core.parser;
/**
* Interface for providing settings for the parser.
* <p>
* The first version of the interface was not marked with no-implement, so methods
* cannot be added to it. This version should be used going forward. It is marked
* no-implement and a Default implementation is provided. Clients should base their
* own implementations on Default in order to avoid being broken by futured additions
* to this interface.
*
* @since 5.7
* @noimplement Extend {@link IParserSettings2.Default} instead.
*/
public interface IParserSettings2 extends IParserSettings {
/**
* An default implementation to be used as a base class by clients that want to
* contribute parser settings. This base provides default values for all methods
* so that clients will still compile when methods are added to the interface.
*
* @noinstantiate This class is not intended to be instantiated by clients.
*/
public static class Default extends ParserSettings implements IParserSettings2 {
@Override
public boolean shouldLimitTokensPerTranslationUnit() {
return false;
}
@Override
public int getMaximumTokensPerTranslationUnit() {
return 0;
}
}
/**
* Returns true if the parser should be aborted when a single translation unit has produced
* more than {@link #getMaximumTokensPerTranslationUnit()} tokens.
*/
public boolean shouldLimitTokensPerTranslationUnit();
/**
* Returns the maximum number of tokens that should be created while parsing any one translation unit.
* This value is used only when {@link #shouldLimitTokensPerTranslationUnit()} returns true.
*/
public int getMaximumTokensPerTranslationUnit();
}

View file

@ -35,6 +35,14 @@ public class ParseError extends Error {
OFFSET_RANGE_NOT_NAME,
TIMEOUT_OR_CANCELLED,
/**
* The user preference for {@link CCorePreferenceConstants#SCALABILITY_LIMIT_TOKENS_PER_TU} is enabled
* and more than {@link CCorePreferenceConstants#SCALABILITY_MAXIMUM_TOKENS} tokens were created while
* parsing a single translation unit.
* @since 5.7
*/
TOO_MANY_TOKENS
}
public ParseErrorKind getErrorKind()
@ -46,4 +54,12 @@ public class ParseError extends Error {
{
errorKind = kind;
}
/**
* @since 5.7
*/
public ParseError(String message, ParseErrorKind kind) {
super(message);
errorKind = kind;
}
}

View file

@ -0,0 +1,64 @@
/*
* Copyright (c) 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
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.cdt.internal.core.parser;
import org.eclipse.cdt.core.CCorePreferenceConstants;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.parser.IParserSettings2;
import org.eclipse.cdt.internal.core.model.CModelManager;
import org.eclipse.core.resources.IProject;
public class ParserSettings2 extends IParserSettings2.Default {
private final boolean limitTokensPerTU;
private final int maxTokensPerTU;
public ParserSettings2() {
this((ICProject) null);
}
public ParserSettings2(IProject project) {
this(CModelManager.getDefault().create(project));
}
/**
* Use the specified project when looking for preferences for the settings object that is
* being constructed.
*
* @param cProject The project from which the settings should be loaded, can be null.
*/
public ParserSettings2(ICProject cProject) {
this.limitTokensPerTU
= CCorePreferenceConstants.getPreference(
CCorePreferenceConstants.SCALABILITY_LIMIT_TOKENS_PER_TU,
cProject,
CCorePreferenceConstants.DEFAULT_SCALABILITY_LIMIT_TOKENS_PER_TU);
this.maxTokensPerTU
= CCorePreferenceConstants.getPreference(
CCorePreferenceConstants.SCALABILITY_MAXIMUM_TOKENS,
cProject,
CCorePreferenceConstants.DEFAULT_SCALABILITY_MAXIMUM_TOKENS);
}
/**
* Returns true if the parser should be aborted when a single translation unit has produced
* more than {@link #getMaximumTokensPerTranslationUnit()} tokens.
*/
@Override
public boolean shouldLimitTokensPerTranslationUnit() {
return limitTokensPerTU;
}
/**
* Returns the maximum number of tokens that should be created while parsing any one translation unit.
* This value will only be used when {@link #shouldLimitTokensPerTranslationUnit()} returns true.
*/
@Override
public int getMaximumTokensPerTranslationUnit() {
return maxTokensPerTU;
}
}

View file

@ -272,6 +272,7 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable {
public CPreprocessor(FileContent fileContent, IScannerInfo info, ParserLanguage language,
IParserLogService log, IScannerExtensionConfiguration configuration,
IncludeFileContentProvider readerFactory) {
Token.resetCounterFor(info);
if (readerFactory instanceof InternalFileContentProvider) {
fFileContentProvider= (InternalFileContentProvider) readerFactory;
} else if (readerFactory == null) {

View file

@ -10,7 +10,12 @@
*******************************************************************************/
package org.eclipse.cdt.internal.core.parser.scanner;
import org.eclipse.cdt.core.parser.ExtendedScannerInfo;
import org.eclipse.cdt.core.parser.IParserSettings;
import org.eclipse.cdt.core.parser.IParserSettings2;
import org.eclipse.cdt.core.parser.IScannerInfo;
import org.eclipse.cdt.core.parser.IToken;
import org.eclipse.cdt.core.parser.ParseError;
/**
* Represents tokens found by the lexer. The preprocessor reuses the tokens and passes
@ -24,7 +29,10 @@ public class Token implements IToken, Cloneable {
private IToken fNextToken;
Object fSource;
private static final Counter tokenCounter = new Counter();
Token(int kind, Object source, int offset, int endOffset) {
tokenCounter.inc();
fKind= kind;
fOffset= offset;
fEndOffset= endOffset;
@ -99,9 +107,59 @@ public class Token implements IToken, Cloneable {
@Override
final public Token clone() {
try {
tokenCounter.inc();
return (Token) super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
}
/**
* Either disable the token counter or reset it to the limit in the given scanner info object.
*/
public static void resetCounterFor(IScannerInfo info) {
tokenCounter.reset(info);
}
/**
* Bug 425711: Some source files cause the CPreprocessor to try to allocate an unmanageable number
* of Tokens. For example, boost has a file, delay.c, that caused over 250 million instances to
* be created -- that is where the VM overflowed my 3Gb heap. Both gcc and clang also ran
* out of memory and crashed while processing that file.
* <p>
* Giving up on a file is better than crashing the entire IDE, so a new user-preference provide
* a way to specify a limit. The preference is implemented by counting the number of instances
* of Token that are created by a single instance of CPreprocessor.
* <p>
* This counter records the total and throws an exception if the limit is surpassed.
*/
private static class Counter {
public int count = 0;
public int limit = -1;
public void reset(IScannerInfo info) {
// The counters are always reset, we optionally apply a new limit if the settings
// are found.
count = 0;
limit = -1;
if (info instanceof ExtendedScannerInfo) {
IParserSettings settings = ((ExtendedScannerInfo) info).getParserSettings();
if (settings instanceof IParserSettings2) {
IParserSettings2 parserSettings = (IParserSettings2) settings;
if (parserSettings.shouldLimitTokensPerTranslationUnit()) {
int maxTokens = parserSettings.getMaximumTokensPerTranslationUnit();
if (maxTokens > 0)
limit = maxTokens;
}
}
}
}
public void inc() throws ParseError {
if (limit > 0
&& ++count > limit)
throw new ParseError(Integer.toString(count) + " tokens", ParseError.ParseErrorKind.TOO_MANY_TOKENS);//$NON-NLS-1$
}
}
}

View file

@ -54,7 +54,6 @@ import org.eclipse.cdt.core.parser.IScannerInfo;
import org.eclipse.cdt.core.parser.ISignificantMacros;
import org.eclipse.cdt.core.parser.IncludeExportPatterns;
import org.eclipse.cdt.core.parser.IncludeFileContentProvider;
import org.eclipse.cdt.core.parser.ParserSettings;
import org.eclipse.cdt.core.parser.ParserUtil;
import org.eclipse.cdt.internal.core.dom.IIncludeFileResolutionHeuristics;
import org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit;
@ -64,6 +63,7 @@ import org.eclipse.cdt.internal.core.index.IIndexFragmentFile;
import org.eclipse.cdt.internal.core.index.IWritableIndex;
import org.eclipse.cdt.internal.core.index.IndexBasedFileContentProvider;
import org.eclipse.cdt.internal.core.parser.IMacroDictionary;
import org.eclipse.cdt.internal.core.parser.ParserSettings2;
import org.eclipse.cdt.internal.core.parser.scanner.InternalFileContentProvider;
import org.eclipse.cdt.internal.core.parser.scanner.InternalFileContentProvider.DependsOnOutdatedFileException;
import org.eclipse.cdt.internal.core.parser.util.LRUCache;
@ -417,7 +417,7 @@ public abstract class AbstractIndexerTask extends PDOMWriter {
}
protected IParserSettings createParserSettings() {
return new ParserSettings();
return new ParserSettings2();
}
/**
@ -1043,7 +1043,9 @@ public abstract class AbstractIndexerTask extends PDOMWriter {
long start= System.currentTimeMillis();
IASTTranslationUnit ast= createAST(lang, codeReader, scanInfo, isSource, fASTOptions, ctx, pm);
fStatistics.fParsingTime += System.currentTimeMillis() - start;
if (ast != null) {
if (ast == null) {
++fStatistics.fTooManyTokensCount;
} else {
// Give the new AST a chance to recognize its translation unit before it is written
// to the index.
((ASTTranslationUnit) ast).setOriginatingTranslationUnit((ITranslationUnit) tu);

View file

@ -22,4 +22,5 @@ public class IndexerStatistics {
public int fUnresolvedIncludesCount= 0;
public int fPreprocessorProblemCount= 0;
public int fSyntaxProblemsCount= 0;
public int fTooManyTokensCount= 0;
}

View file

@ -282,6 +282,9 @@ public abstract class PDOMIndexerTask extends AbstractIndexerTask implements IPD
+ fStatistics.fUnresolvedIncludesCount + " include, " //$NON-NLS-1$
+ fStatistics.fPreprocessorProblemCount + " scanner, " //$NON-NLS-1$
+ fStatistics.fSyntaxProblemsCount + " syntax errors."); //$NON-NLS-1$
if (fStatistics.fTooManyTokensCount > 0)
System.out.println(ident + " Tokens: " //$NON-NLS-1$
+ fStatistics.fTooManyTokensCount + " TUs with too many tokens."); //$NON-NLS-1$
NumberFormat nfPercent= NumberFormat.getPercentInstance();
nfPercent.setMaximumFractionDigits(2);

View file

@ -250,6 +250,39 @@ public class CCorePreferenceConstants {
*/
public static final int DEFAULT_SCALABILITY_MAXIMUM_TRIVIAL_EXPRESSIONS = 1000;
/**
* A named preference that specifies whether the parser should abort when too many Tokens are created
* during parse of a single TU. This is a heuristic that is used to detect translation units that
* are too complex to be handled the by the CDT parser.
*
* @since 5.7
*/
public static final String SCALABILITY_LIMIT_TOKENS_PER_TU = "scalability.limitTokensPerTU"; //$NON-NLS-1$
/**
* Default value for {@link #SCALABILITY_LIMIT_TOKENS_PER_TU}.
* @since 5.7
*/
public static final boolean DEFAULT_SCALABILITY_LIMIT_TOKENS_PER_TU = false;
/**
* A named preference that specifies the parser's token limit. Parsing will be aborted when a single
* translation unit has produced a maximum number of tokens. This is a heuristic that is used to
* detect translation units that are too complex to be handled the by the CDT parser.
*
* @since 5.7
*/
public static final String SCALABILITY_MAXIMUM_TOKENS = "scalability.maximumTokens"; //$NON-NLS-1$
/**
* Default value for {@link #SCALABILITY_MAXIMUM_TOKENS}.
*
* @since 5.7
*/
public static final int DEFAULT_SCALABILITY_MAXIMUM_TOKENS = 25 * 1000 * 1000;
// NOTE: This default came from measurements using a 1Gb heap on a 64-bit VM. The test project was
// boost-1.55.0. This default will index all but 9 files without running out of memory.
/**
* Returns the node in the preference in the given context.
* @param key The preference key.

View file

@ -71,5 +71,7 @@ public class CCorePreferenceInitializer extends AbstractPreferenceInitializer {
// Scalability defaults.
defaultPreferences.putBoolean(CCorePreferenceConstants.SCALABILITY_SKIP_TRIVIAL_EXPRESSIONS, CCorePreferenceConstants.DEFAULT_SCALABILITY_SKIP_TRIVIAL_EXPRESSIONS);
defaultPreferences.putInt(CCorePreferenceConstants.SCALABILITY_MAXIMUM_TRIVIAL_EXPRESSIONS, CCorePreferenceConstants.DEFAULT_SCALABILITY_MAXIMUM_TRIVIAL_EXPRESSIONS);
defaultPreferences.putBoolean(CCorePreferenceConstants.SCALABILITY_LIMIT_TOKENS_PER_TU, CCorePreferenceConstants.DEFAULT_SCALABILITY_LIMIT_TOKENS_PER_TU);
defaultPreferences.putInt(CCorePreferenceConstants.SCALABILITY_MAXIMUM_TOKENS, CCorePreferenceConstants.DEFAULT_SCALABILITY_MAXIMUM_TOKENS);
}
}

View file

@ -555,6 +555,7 @@ public final class PreferencesMessages extends NLS {
public static String ScalabilityPreferencePage_parserSettings_group_label;
public static String ScalabilityPreferencePage_skipTrivialExpressions_label;
public static String ScalabilityPreferencePage_maximumTrivialExpressions_label;
public static String ScalabilityPreferencePage_maximumTokensPerTU_label;
public static String IndexerStrategyBlock_activeBuildConfig;
public static String IndexerStrategyBlock_autoUpdate;

View file

@ -621,6 +621,7 @@ ScalabilityPreferencePage_preferenceOnlyForNewEditors=Some options do not affect
ScalabilityPreferencePage_parserSettings_group_label= Parser settings
ScalabilityPreferencePage_skipTrivialExpressions_label= Skip trivial expressions in initializer lists
ScalabilityPreferencePage_maximumTrivialExpressions_label= Maximum number of trivial expressions in initializer lists to parse:
ScalabilityPreferencePage_maximumTokensPerTU_label= Maximum number of tokens per translation unit:
IndexerStrategyBlock_strategyGroup=Indexing strategy
IndexerStrategyBlock_autoUpdate=Automatically update the index

View file

@ -0,0 +1,195 @@
/*
* Copyright (c) 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
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.cdt.internal.ui.preferences;
import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.jface.fieldassist.FieldDecoration;
import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
import org.eclipse.jface.preference.IntegerFieldEditor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
/**
* A field editor that combines an integer value preference with a boolean enablement
* preference.
*
* @since 5.8
*/
public class ScalabilityIntegerFieldEditor extends IntegerFieldEditor {
private ControlDecoration fDecoration;
private final String fEnableKey;
private Button fCheckbox;
private boolean fWasSelected;
public ScalabilityIntegerFieldEditor(String enableKey, String nameKey, String labelText, Composite parent) {
super(nameKey, labelText, parent);
fEnableKey= enableKey;
}
@Override
public Text getTextControl(Composite parent) {
Text control = super.getTextControl(parent);
if (fDecoration == null) {
fDecoration = new ControlDecoration(control, SWT.LEFT | SWT.TOP);
FieldDecoration errorDecoration = FieldDecorationRegistry.getDefault().getFieldDecoration(FieldDecorationRegistry.DEC_ERROR);
fDecoration.setImage(errorDecoration.getImage());
fDecoration.setDescriptionText(getErrorMessage());
// validate on focus gain
control.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
refreshValidState();
}
});
}
return control;
}
@Override
protected void showErrorMessage(String msg) {
super.showErrorMessage(msg);
if (fDecoration != null) {
fDecoration.setDescriptionText(msg);
fDecoration.show();
}
}
@Override
protected void clearErrorMessage() {
super.clearErrorMessage();
if (fDecoration != null) {
fDecoration.hide();
}
}
@Override
protected void doFillIntoGrid(Composite parent, int numColumns) {
getCheckboxControl(parent);
super.doFillIntoGrid(parent, numColumns);
}
private Button getCheckboxControl(Composite parent) {
if (fCheckbox == null) {
Composite inner= new Composite(parent, SWT.NULL);
final GridLayout layout= new GridLayout(2, false);
layout.marginWidth = 0;
inner.setLayout(layout);
fCheckbox= new Button(inner, SWT.CHECK);
fCheckbox.setFont(parent.getFont());
fCheckbox.setText(getLabelText());
// create and hide label from base class
Label label = getLabelControl(inner);
label.setText(""); //$NON-NLS-1$
label.setVisible(false);
fCheckbox.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
boolean isSelected = fCheckbox.getSelection();
valueChanged(fWasSelected, isSelected);
fWasSelected = isSelected;
}
});
} else {
checkParent(fCheckbox.getParent(), parent);
}
return fCheckbox;
}
@Override
public Label getLabelControl(Composite parent) {
final Label label= getLabelControl();
if (label == null) {
return super.getLabelControl(parent);
} else {
checkParent(label.getParent(), parent);
}
return label;
}
protected void valueChanged(boolean oldValue, boolean newValue) {
if (oldValue != newValue) {
valueChanged();
fireStateChanged(VALUE, oldValue, newValue);
getTextControl().setEnabled(newValue);
getLabelControl().setEnabled(newValue);
}
}
@Override
protected boolean checkState() {
if (fCheckbox != null && !fCheckbox.getSelection()) {
clearErrorMessage();
return true;
}
return super.checkState();
}
@Override
protected void doLoad() {
super.doLoad();
if (fCheckbox != null) {
boolean value = getPreferenceStore().getBoolean(fEnableKey);
fCheckbox.setSelection(value);
fWasSelected = value;
getTextControl().setEnabled(value);
getLabelControl().setEnabled(value);
}
}
@Override
protected void doLoadDefault() {
super.doLoadDefault();
if (fCheckbox != null) {
boolean value = getPreferenceStore().getDefaultBoolean(fEnableKey);
fCheckbox.setSelection(value);
fWasSelected = value;
getTextControl().setEnabled(value);
getLabelControl().setEnabled(value);
}
}
@Override
protected void doStore() {
super.doStore();
getPreferenceStore().setValue(fEnableKey, fCheckbox.getSelection());
}
/**
* Returns this field editor's current boolean value.
*
* @return the value
*/
public boolean getBooleanValue() {
return fCheckbox.getSelection();
}
/**
* Set the checkbox selection and enablement of the other controls as specified.
*/
public void setBooleanValue(boolean value) {
// The checkbox selection will normally be propagated to the label and text controls in the
// checkbox selection listener. However, the callback is only invoked when the selection changes,
// which means that an initial value of false will not be properly propagated. The state is
// directly updated here.
if (fCheckbox != null) {
fWasSelected = value;
getLabelControl().setEnabled(value);
getTextControl().setEnabled(value);
fCheckbox.setSelection(value);
}
}
}

View file

@ -19,7 +19,6 @@ import java.util.Map;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.preference.BooleanFieldEditor;
import org.eclipse.jface.preference.FieldEditor;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.IntegerFieldEditor;
@ -63,14 +62,12 @@ public class ScalabilityPreferencePage extends PreferencePage implements IWorkbe
private Button fSemanticHighlighting;
private Button fContentAssist;
private Button fContentAssistAutoActivation;
private BooleanFieldEditor fSkipTrivialExpressions;
private ScalabilityIntegerFieldEditor fMaximumTrivialExpressions;
private IntegerFieldEditor fMaximumTrivialExpressions;
private Composite fParserSettingsComposite;
private ScalabilityIntegerFieldEditor fMaximumTokens;
private final Map<Object, String> fCheckBoxes= new HashMap<Object, String>();
@ -116,10 +113,8 @@ public class ScalabilityPreferencePage extends PreferencePage implements IWorkbe
listener.widgetSelected(null);
}
fLinesToTrigger.setStringValue(Integer.toString(prefs.getInt(PreferenceConstants.SCALABILITY_NUMBER_OF_LINES)));
fSkipTrivialExpressions.load();
fMaximumTrivialExpressions.load();
updateEnable();
fMaximumTokens.load();
}
/*
@ -162,23 +157,23 @@ public class ScalabilityPreferencePage extends PreferencePage implements IWorkbe
createParserSettings(composite);
new Separator().doFillIntoGrid(composite, nColumns);
String noteTitle= PreferencesMessages.ScalabilityPreferencePage_note;
String noteMessage= PreferencesMessages.ScalabilityPreferencePage_preferenceOnlyForNewEditors;
Composite noteControl= createNoteComposite(JFaceResources.getDialogFont(), composite, noteTitle, noteMessage);
GridData gd= new GridData(GridData.HORIZONTAL_ALIGN_FILL);
gd.horizontalSpan= 2;
noteControl.setLayoutData(gd);
initFields();
Dialog.applyDialogFont(composite);
return composite;
}
/**
* Creates composite group and sets the default layout data.
*
*
* @param parent
* the parent of the new composite
* @param numColumns
@ -190,7 +185,7 @@ public class ScalabilityPreferencePage extends PreferencePage implements IWorkbe
private Composite createGroupComposite( Composite parent, int numColumns, String labelText ) {
return ControlFactory.createGroup( parent, labelText, numColumns );
}
/**
* Create the view setting preferences composite widget
*/
@ -228,30 +223,25 @@ public class ScalabilityPreferencePage extends PreferencePage implements IWorkbe
}
private void createParserSettings(Composite parent) {
final Composite parserSettingsGroup = createGroupComposite(parent, 1,
PreferencesMessages.ScalabilityPreferencePage_parserSettings_group_label);
Composite group = createGroupComposite( parent, 1, PreferencesMessages.ScalabilityPreferencePage_parserSettings_group_label );
final Composite skipTrivialComposite = new Composite(parserSettingsGroup, SWT.NONE);
fSkipTrivialExpressions = new BooleanFieldEditor(CCorePreferenceConstants.SCALABILITY_SKIP_TRIVIAL_EXPRESSIONS,
PreferencesMessages.ScalabilityPreferencePage_skipTrivialExpressions_label, skipTrivialComposite);
fSkipTrivialExpressions.setPreferenceStore(CUIPlugin.getDefault().getCorePreferenceStore());
fParserSettingsComposite = new Composite(parserSettingsGroup, SWT.NONE);
fMaximumTrivialExpressions = createIntegerField(fParserSettingsComposite,
CCorePreferenceConstants.SCALABILITY_MAXIMUM_TRIVIAL_EXPRESSIONS,
PreferencesMessages.ScalabilityPreferencePage_maximumTrivialExpressions_label, 0, Integer.MAX_VALUE);
fMaximumTrivialExpressions
= createScalabilityIntegerField(
group,
CCorePreferenceConstants.SCALABILITY_SKIP_TRIVIAL_EXPRESSIONS,
CCorePreferenceConstants.SCALABILITY_MAXIMUM_TRIVIAL_EXPRESSIONS,
PreferencesMessages.ScalabilityPreferencePage_skipTrivialExpressions_label,
1, Integer.MAX_VALUE);
fMaximumTrivialExpressions.setPreferenceStore(CUIPlugin.getDefault().getCorePreferenceStore());
fSkipTrivialExpressions.setPropertyChangeListener(new IPropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent event) {
updateEnable();
}
});
}
private void updateEnable() {
fMaximumTrivialExpressions.setEnabled(fSkipTrivialExpressions.getBooleanValue(), fParserSettingsComposite);
fMaximumTokens
= createScalabilityIntegerField(
group,
CCorePreferenceConstants.SCALABILITY_LIMIT_TOKENS_PER_TU,
CCorePreferenceConstants.SCALABILITY_MAXIMUM_TOKENS,
PreferencesMessages.ScalabilityPreferencePage_maximumTokensPerTU_label,
1, 1000000000);
fMaximumTokens.setPreferenceStore(CUIPlugin.getDefault().getCorePreferenceStore());
}
private IntegerFieldEditor createIntegerField(Composite parent, String name, String labelText, int rangeMinimum, int rangeMaximum) {
@ -276,6 +266,28 @@ public class ScalabilityPreferencePage extends PreferencePage implements IWorkbe
return integerField;
}
private ScalabilityIntegerFieldEditor createScalabilityIntegerField(Composite parent, String enable, String name, String labelText, int rangeMinimum, int rangeMaximum) {
final ScalabilityIntegerFieldEditor field = new ScalabilityIntegerFieldEditor(enable, name, labelText, parent);
GridData data = (GridData) field.getTextControl(parent).getLayoutData();
data.horizontalAlignment = GridData.BEGINNING;
data.widthHint = convertWidthInCharsToPixels(11);
field.setPage(this);
field.setValidateStrategy(StringFieldEditor.VALIDATE_ON_KEY_STROKE);
field.setValidRange(rangeMinimum, rangeMaximum);
field.setErrorMessage(NLS.bind(PreferencesMessages.ScalabilityPreferencePage_error,
new Object[] { rangeMinimum, rangeMaximum }));
field.load();
field.setPropertyChangeListener(new IPropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent event) {
if (event.getProperty().equals(FieldEditor.IS_VALID))
setValid(field.isValid());
}
});
return field;
}
private static void indent(Control control, GridData masterLayoutData) {
GridData gridData= new GridData();
gridData.horizontalIndent= masterLayoutData.horizontalIndent + 20;
@ -329,8 +341,8 @@ public class ScalabilityPreferencePage extends PreferencePage implements IWorkbe
prefs.setValue(key, b.getSelection());
}
prefs.setValue(PreferenceConstants.SCALABILITY_NUMBER_OF_LINES, fLinesToTrigger.getIntValue());
fSkipTrivialExpressions.store();
fMaximumTrivialExpressions.store();
fMaximumTokens.store();
return super.performOk();
}
@ -355,8 +367,7 @@ public class ScalabilityPreferencePage extends PreferencePage implements IWorkbe
listener.widgetSelected(null);
}
fLinesToTrigger.setStringValue(Integer.toString(prefs.getDefaultInt(PreferenceConstants.SCALABILITY_NUMBER_OF_LINES)));
fSkipTrivialExpressions.loadDefault();
fMaximumTrivialExpressions.loadDefault();
updateEnable();
fMaximumTokens.loadDefault();
}
}