diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsScannerInfoProvider.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsScannerInfoProvider.java index 13d41d28c30..d54236e7e83 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsScannerInfoProvider.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsScannerInfoProvider.java @@ -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(); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/parser/AbstractCLikeLanguage.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/parser/AbstractCLikeLanguage.java index 14000e094e7..817e1c5ed01 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/parser/AbstractCLikeLanguage.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/parser/AbstractCLikeLanguage.java @@ -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); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/IParserSettings2.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/IParserSettings2.java new file mode 100644 index 00000000000..05fd75d379c --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/IParserSettings2.java @@ -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. + *

+ * 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(); +} diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/ParseError.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/ParseError.java index 6836d59978b..dc56f5e7461 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/ParseError.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/ParseError.java @@ -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; + } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/ParserSettings2.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/ParserSettings2.java new file mode 100644 index 00000000000..6296923e4b7 --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/ParserSettings2.java @@ -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; + } +} diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java index 1292102d999..b4fd64f5d54 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java @@ -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) { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/Token.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/Token.java index 46174108f57..ca856508b3e 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/Token.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/Token.java @@ -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. + *

+ * 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. + *

+ * 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$ + } + } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/AbstractIndexerTask.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/AbstractIndexerTask.java index 6b7f05d423d..3b357eb1561 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/AbstractIndexerTask.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/AbstractIndexerTask.java @@ -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); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/IndexerStatistics.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/IndexerStatistics.java index b96732ad75e..7c6be5ee874 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/IndexerStatistics.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/IndexerStatistics.java @@ -22,4 +22,5 @@ public class IndexerStatistics { public int fUnresolvedIncludesCount= 0; public int fPreprocessorProblemCount= 0; public int fSyntaxProblemsCount= 0; + public int fTooManyTokensCount= 0; } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/PDOMIndexerTask.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/PDOMIndexerTask.java index 3be9be0944f..5699d9c0234 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/PDOMIndexerTask.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/PDOMIndexerTask.java @@ -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); diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePreferenceConstants.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePreferenceConstants.java index 861537e43aa..02f8b5f78f6 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePreferenceConstants.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePreferenceConstants.java @@ -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. diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/CCorePreferenceInitializer.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/CCorePreferenceInitializer.java index 587bc70d905..4a5ac51711d 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/CCorePreferenceInitializer.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/CCorePreferenceInitializer.java @@ -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); } } 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 92fb421dbd1..6d0de34088e 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 @@ -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; 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 9b605b5458f..b5bafa444b4 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 @@ -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 diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/ScalabilityIntegerFieldEditor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/ScalabilityIntegerFieldEditor.java new file mode 100644 index 00000000000..9014e3b94e3 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/ScalabilityIntegerFieldEditor.java @@ -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); + } + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/ScalabilityPreferencePage.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/ScalabilityPreferencePage.java index 7bef09509b6..f1503196d2e 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/ScalabilityPreferencePage.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/ScalabilityPreferencePage.java @@ -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 fCheckBoxes= new HashMap(); @@ -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(); } }