diff --git a/org.eclipse.tm4e.core/META-INF/MANIFEST.MF b/org.eclipse.tm4e.core/META-INF/MANIFEST.MF index 3379a6cfd..11a8a90ef 100644 --- a/org.eclipse.tm4e.core/META-INF/MANIFEST.MF +++ b/org.eclipse.tm4e.core/META-INF/MANIFEST.MF @@ -4,7 +4,7 @@ Bundle-Name: %pluginName Bundle-Vendor: %providerName Bundle-Localization: plugin Bundle-SymbolicName: org.eclipse.tm4e.core -Bundle-Version: 0.5.4.qualifier +Bundle-Version: 0.5.5.qualifier Require-Bundle: org.apache.batik.css;bundle-version="1.9.1";resolution:=optional, org.apache.batik.util;bundle-version="1.9.1";resolution:=optional, com.google.gson;bundle-version="2.9.0", diff --git a/org.eclipse.tm4e.core/pom.xml b/org.eclipse.tm4e.core/pom.xml index 18fce7ef8..e306aa8a3 100644 --- a/org.eclipse.tm4e.core/pom.xml +++ b/org.eclipse.tm4e.core/pom.xml @@ -10,7 +10,7 @@ org.eclipse.tm4e.core eclipse-plugin - 0.5.4-SNAPSHOT + 0.5.5-SNAPSHOT diff --git a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/grammar/AttributedScopeStack.java b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/grammar/AttributedScopeStack.java index 9ab2c9865..a16449979 100644 --- a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/grammar/AttributedScopeStack.java +++ b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/grammar/AttributedScopeStack.java @@ -23,8 +23,7 @@ import org.eclipse.tm4e.core.internal.grammar.tokenattrs.EncodedTokenAttributes; import org.eclipse.tm4e.core.internal.theme.FontStyle; import org.eclipse.tm4e.core.internal.theme.StyleAttributes; - -import com.google.common.base.Splitter; +import org.eclipse.tm4e.core.internal.utils.StringUtils; /** * @see scopeNames) { } - private static final Splitter BY_SPACE_SPLITTER = Splitter.on(' '); - @Nullable static AttributedScopeStack fromExtension(final @Nullable AttributedScopeStack namesScopeList, final List contentNameScopesList) { @@ -160,7 +157,7 @@ AttributedScopeStack pushAttributed(final @Nullable String scopePath, final Gram return _pushAttributed(this, scopePath, grammar); } - final var scopes = BY_SPACE_SPLITTER.split(scopePath); + final var scopes = StringUtils.splitToArray(scopePath, ' '); var result = this; for (final var scope : scopes) { result = _pushAttributed(result, scope, grammar); diff --git a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/grammar/BalancedBracketSelectors.java b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/grammar/BalancedBracketSelectors.java index 2dad57b6e..94f5be1b2 100644 --- a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/grammar/BalancedBracketSelectors.java +++ b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/grammar/BalancedBracketSelectors.java @@ -27,7 +27,7 @@ * "https://github.com/microsoft/vscode-textmate/blob/88baacf1a6637c5ec08dce18cea518d935fcf0a0/src/grammar/grammar.ts#L898"> * github.com/microsoft/vscode-textmate/blob/main/src/grammar/grammar.ts */ -public class BalancedBracketSelectors { +public final class BalancedBracketSelectors { private final Matcher>[] balancedBracketScopes; private final Matcher>[] unbalancedBracketScopes; diff --git a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/grammar/RawCaptures.java b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/grammar/RawCaptures.java index 8c49ce48c..4599b5ae7 100644 --- a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/grammar/RawCaptures.java +++ b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/grammar/RawCaptures.java @@ -15,7 +15,7 @@ import org.eclipse.tm4e.core.internal.types.IRawCaptures; import org.eclipse.tm4e.core.internal.types.IRawRule; -public class RawCaptures extends HashMap implements IRawCaptures, PropertySettable { +public final class RawCaptures extends HashMap implements IRawCaptures, PropertySettable { private static final long serialVersionUID = 1L; diff --git a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/grammar/RawGrammar.java b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/grammar/RawGrammar.java index ef3256cb8..daf5228bb 100644 --- a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/grammar/RawGrammar.java +++ b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/grammar/RawGrammar.java @@ -26,7 +26,7 @@ import org.eclipse.tm4e.core.internal.types.IRawRepository; import org.eclipse.tm4e.core.internal.types.IRawRule; -public class RawGrammar extends HashMap implements IRawGrammar, PropertySettable { +public final class RawGrammar extends HashMap implements IRawGrammar, PropertySettable { private static final String FILE_TYPES = "fileTypes"; private static final String FIRST_LINE_MATCH = "firstLineMatch"; diff --git a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/grammar/RawRepository.java b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/grammar/RawRepository.java index 639afd89d..f20b63abe 100644 --- a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/grammar/RawRepository.java +++ b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/grammar/RawRepository.java @@ -19,7 +19,7 @@ import org.eclipse.tm4e.core.internal.types.IRawRepository; import org.eclipse.tm4e.core.internal.types.IRawRule; -public class RawRepository extends HashMap implements IRawRepository, PropertySettable { +public final class RawRepository extends HashMap implements IRawRepository, PropertySettable { private static final long serialVersionUID = 1L; diff --git a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/grammar/ScopeStack.java b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/grammar/ScopeStack.java index 8db37d1a9..19b962dee 100644 --- a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/grammar/ScopeStack.java +++ b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/grammar/ScopeStack.java @@ -27,7 +27,7 @@ * "https://github.com/microsoft/vscode-textmate/blob/88baacf1a6637c5ec08dce18cea518d935fcf0a0/src/theme.ts#L101"> * github.com/microsoft/vscode-textmate/blob/main/src/theme.ts */ -public class ScopeStack { +public final class ScopeStack { @Nullable static ScopeStack push(@Nullable ScopeStack path, final List scopeNames) { diff --git a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/grammar/dependencies/ScopeDependencyProcessor.java b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/grammar/dependencies/ScopeDependencyProcessor.java index 28de67197..717fcfd4e 100644 --- a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/grammar/dependencies/ScopeDependencyProcessor.java +++ b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/grammar/dependencies/ScopeDependencyProcessor.java @@ -40,7 +40,7 @@ * "https://github.com/microsoft/vscode-textmate/blob/88baacf1a6637c5ec08dce18cea518d935fcf0a0/src/grammar/grammarDependencies.ts#L59"> * github.com/microsoft/vscode-textmate/blob/main/src/grammar/grammarDependencies.ts */ -public class ScopeDependencyProcessor { +public final class ScopeDependencyProcessor { private static class ExternalReferenceCollector { diff --git a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/Theme.java b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/Theme.java index 9806efe76..cc4e2987c 100644 --- a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/Theme.java +++ b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/Theme.java @@ -23,8 +23,8 @@ import org.eclipse.jdt.annotation.Nullable; import org.eclipse.tm4e.core.internal.grammar.ScopeStack; +import org.eclipse.tm4e.core.internal.utils.StringUtils; -import com.google.common.base.Splitter; import com.google.common.collect.Lists; /** @@ -36,9 +36,6 @@ */ public final class Theme { - private static final Splitter BY_COMMA_SPLITTER = Splitter.on(','); - private static final Splitter BY_SPACE_SPLITTER = Splitter.on(' '); - public static Theme createFromRawTheme(@Nullable final IRawTheme source, @Nullable final List colorMap) { return createFromParsedTheme(parseTheme(source), colorMap); } @@ -147,7 +144,7 @@ public static List parseTheme(@Nullable final IRawTheme source) // remove trailing commas _scope = _scope.replaceAll(",+$", ""); - scopes = BY_COMMA_SPLITTER.splitToList(_scope); + scopes = StringUtils.splitToList(_scope, ','); } else if (settingScope instanceof List) { @SuppressWarnings("unchecked") final var settingScopes = (List) settingScope; @@ -161,7 +158,7 @@ public static List parseTheme(@Nullable final IRawTheme source) if (settingsFontStyle instanceof final String style) { fontStyle = FontStyle.None; - final var segments = BY_SPACE_SPLITTER.split(style); + final var segments = StringUtils.splitToArray(style, ' '); for (final var segment : segments) { switch (segment) { case "italic": @@ -197,7 +194,7 @@ && isValidHexColor(stringSettingsBackground)) { for (int j = 0, lenJ = scopes.size(); j < lenJ; j++) { final var _scope = scopes.get(j).trim(); - final var segments = BY_SPACE_SPLITTER.splitToList(_scope); + final var segments = StringUtils.splitToList(_scope, ' '); final var scope = getLastElement(segments); List parentScopes = null; diff --git a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/CSSValueImpl.java b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/AbstractCSSValue.java similarity index 97% rename from org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/CSSValueImpl.java rename to org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/AbstractCSSValue.java index 4c963c29c..287b21192 100644 --- a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/CSSValueImpl.java +++ b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/AbstractCSSValue.java @@ -18,7 +18,7 @@ import org.w3c.dom.css.RGBColor; import org.w3c.dom.css.Rect; -abstract class CSSValueImpl implements CSSPrimitiveValue { +abstract class AbstractCSSValue implements CSSPrimitiveValue { // FIXME rename // W3C CSSValue API methods diff --git a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/AbstractCombinatorCondition.java b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/AbstractCombinatorCondition.java index b94c7d03e..eafff5d70 100644 --- a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/AbstractCombinatorCondition.java +++ b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/AbstractCombinatorCondition.java @@ -16,13 +16,13 @@ public abstract class AbstractCombinatorCondition implements CombinatorCondition, ExtendedCondition { - private final Condition firstCondition; - private final Condition secondCondition; + protected final ExtendedCondition firstCondition; + protected final ExtendedCondition secondCondition; /** * Creates a new CombinatorCondition object. */ - protected AbstractCombinatorCondition(final Condition c1, final Condition c2) { + protected AbstractCombinatorCondition(final ExtendedCondition c1, final ExtendedCondition c2) { firstCondition = c1; secondCondition = c2; } @@ -39,7 +39,6 @@ public Condition getSecondCondition() { @Override public int getSpecificity() { - return ((ExtendedCondition) getFirstCondition()).getSpecificity() - + ((ExtendedCondition) getSecondCondition()).getSpecificity(); + return firstCondition.getSpecificity() + secondCondition.getSpecificity(); } } diff --git a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/CSSAndCondition.java b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/CSSAndCondition.java index 72a2fceee..cf552cea3 100644 --- a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/CSSAndCondition.java +++ b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/CSSAndCondition.java @@ -11,14 +11,12 @@ */ package org.eclipse.tm4e.core.internal.theme.css; -import org.w3c.css.sac.Condition; - final class CSSAndCondition extends AbstractCombinatorCondition { /** * Creates a new CombinatorCondition object. */ - CSSAndCondition(final Condition c1, final Condition c2) { + CSSAndCondition(final ExtendedCondition c1, final ExtendedCondition c2) { super(c1, c2); } @@ -29,13 +27,11 @@ public short getConditionType() { @Override public int nbMatch(final String... names) { - return ((ExtendedCondition) getFirstCondition()).nbMatch(names) - + ((ExtendedCondition) getSecondCondition()).nbMatch(names); + return firstCondition.nbMatch(names) + secondCondition.nbMatch(names); } @Override public int nbClass() { - return ((ExtendedCondition) getFirstCondition()).nbClass() - + ((ExtendedCondition) getSecondCondition()).nbClass(); + return firstCondition.nbClass() + secondCondition.nbClass(); } } diff --git a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/CSSConditionFactory.java b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/CSSConditionFactory.java index cab7041c0..43820854c 100644 --- a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/CSSConditionFactory.java +++ b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/CSSConditionFactory.java @@ -40,7 +40,7 @@ public AttributeCondition createAttributeCondition(final String localName, final @Override public CombinatorCondition createAndCondition(final Condition first, final Condition second) throws CSSException { - return new CSSAndCondition(first, second); + return new CSSAndCondition((ExtendedCondition) first, (ExtendedCondition) second); } @Override diff --git a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/CSSConditionalSelector.java b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/CSSConditionalSelector.java index 17064cc10..b902a9daa 100644 --- a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/CSSConditionalSelector.java +++ b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/CSSConditionalSelector.java @@ -17,21 +17,11 @@ final class CSSConditionalSelector implements ConditionalSelector, ExtendedSelector { - /** - * The simple selector. - */ - private final SimpleSelector simpleSelector; + private final ExtendedSelector selector; + private final ExtendedCondition condition; - /** - * The condition. - */ - private final Condition condition; - - /** - * Creates a new ConditionalSelector object. - */ - CSSConditionalSelector(final SimpleSelector simpleSelector, final Condition condition) { - this.simpleSelector = simpleSelector; + CSSConditionalSelector(final ExtendedSelector simpleSelector, final ExtendedCondition condition) { + this.selector = simpleSelector; this.condition = condition; } @@ -47,25 +37,21 @@ public Condition getCondition() { @Override public SimpleSelector getSimpleSelector() { - return simpleSelector; + return selector; } @Override public int getSpecificity() { - return ((ExtendedSelector) getSimpleSelector()).getSpecificity() - + ((ExtendedCondition) getCondition()).getSpecificity(); + return selector.getSpecificity() + condition.getSpecificity(); } @Override public int nbMatch(final String... names) { - return ((ExtendedSelector) getSimpleSelector()).nbMatch(names) - + ((ExtendedCondition) getCondition()).nbMatch(names); + return selector.nbMatch(names) + condition.nbMatch(names); } @Override public int nbClass() { - return ((ExtendedSelector) getSimpleSelector()).nbClass() - + ((ExtendedCondition) getCondition()).nbClass(); + return selector.nbClass() + condition.nbClass(); } - } diff --git a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/CSSSelectorFactory.java b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/CSSSelectorFactory.java index 667a61db0..b7176157e 100644 --- a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/CSSSelectorFactory.java +++ b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/CSSSelectorFactory.java @@ -55,7 +55,7 @@ public CharacterDataSelector createCommentSelector(final String arg0) throws CSS @Override public ConditionalSelector createConditionalSelector(final SimpleSelector selector, final Condition condition) throws CSSException { - return new CSSConditionalSelector(selector, condition); + return new CSSConditionalSelector((ExtendedSelector) selector, (ExtendedCondition) condition); } @Override diff --git a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/ExtendedCondition.java b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/ExtendedCondition.java index c65bbc70a..c4c375996 100644 --- a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/ExtendedCondition.java +++ b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/ExtendedCondition.java @@ -11,7 +11,9 @@ */ package org.eclipse.tm4e.core.internal.theme.css; -interface ExtendedCondition { +import org.w3c.css.sac.Condition; + +interface ExtendedCondition extends Condition { /** * Returns the specificity of this condition. diff --git a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/ExtendedSelector.java b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/ExtendedSelector.java index 5896449c1..c8457bead 100644 --- a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/ExtendedSelector.java +++ b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/ExtendedSelector.java @@ -11,9 +11,9 @@ */ package org.eclipse.tm4e.core.internal.theme.css; -import org.w3c.css.sac.Selector; +import org.w3c.css.sac.SimpleSelector; -public interface ExtendedSelector extends Selector { +public interface ExtendedSelector extends SimpleSelector { /** * Returns the specificity of this selector. diff --git a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/Measure.java b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/Measure.java index 687dca7d0..635976f63 100644 --- a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/Measure.java +++ b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/Measure.java @@ -14,7 +14,7 @@ import org.w3c.dom.DOMException; import org.w3c.dom.css.CSSValue; -final class Measure extends CSSValueImpl { +final class Measure extends AbstractCSSValue { private final LexicalUnit value; diff --git a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/RGBColorImpl.java b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/RGBColorImpl.java index aa4802c91..a006e90f6 100644 --- a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/RGBColorImpl.java +++ b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/theme/css/RGBColorImpl.java @@ -16,7 +16,7 @@ import org.w3c.dom.css.CSSPrimitiveValue; import org.w3c.dom.css.RGBColor; -final class RGBColorImpl extends CSSValueImpl implements RGBColor { +final class RGBColorImpl extends AbstractCSSValue implements RGBColor { private final CSSPrimitiveValue red; private final CSSPrimitiveValue green; diff --git a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/utils/StringUtils.java b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/utils/StringUtils.java index 48d0ff2a5..87aad2b99 100644 --- a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/utils/StringUtils.java +++ b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/utils/StringUtils.java @@ -13,9 +13,11 @@ * Contributors: * - Microsoft Corporation: Initial code, written in TypeScript, licensed under MIT license * - Angelo Zerr - translation and adaptation to Java + * - Sebastian Thomschke - add splitToArray/List methods */ package org.eclipse.tm4e.core.internal.utils; +import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; import java.util.regex.Pattern; @@ -29,6 +31,8 @@ */ public final class StringUtils { + private static final List LIST_WITH_EMPTY_STRING = List.of(""); + private static final Pattern RRGGBB = Pattern.compile("^#[0-9a-f]{6}", Pattern.CASE_INSENSITIVE); private static final Pattern RRGGBBAA = Pattern.compile("^#[0-9a-f]{8}", Pattern.CASE_INSENSITIVE); private static final Pattern RGB = Pattern.compile("^#[0-9a-f]{3}", Pattern.CASE_INSENSITIVE); @@ -62,6 +66,65 @@ public static boolean isValidHexColor(final CharSequence hex) { return false; } + /** + * Very fast String splitting. + * + * 7.5 times faster than {@link String#split(String)} and 2.5 times faster than {@link com.google.common.base.Splitter}. + */ + public static String[] splitToArray(final String line, final char separator) { + if (line.isEmpty()) + return new String[] { "" }; + + var tmp = new String[8]; + int count = 0; + int start = 0; + int end = line.indexOf(separator, 0); + while (end >= 0) { + if (count == tmp.length) { // check if array needs resize + final var tmp2 = new String[tmp.length + (tmp.length >> 1)]; + System.arraycopy(tmp, 0, tmp2, 0, count); + tmp = tmp2; + } + tmp[count] = line.substring(start, end); + count++; + start = end + 1; + end = line.indexOf(separator, start); + } + if (count == tmp.length) { // check if array needs resize + final var tmp2 = new String[tmp.length + 1]; + System.arraycopy(tmp, 0, tmp2, 0, count); + tmp = tmp2; + } + tmp[count] = line.substring(start); + count++; + + if (count == tmp.length) { + return tmp; + } + final var result = new String[count]; + System.arraycopy(tmp, 0, result, 0, count); + return result; + } + + /** + * Very fast String splitting. + */ + public static List splitToList(final String line, final char separator) { + if (line.isEmpty()) + return LIST_WITH_EMPTY_STRING; + + final var result = new ArrayList(8); + int start = 0; + int end = line.indexOf(separator, 0); + while (end >= 0) { + result.add(line.substring(start, end)); + start = end + 1; + end = line.indexOf(separator, start); + } + result.add(line.substring(start)); + return result; + } + public static int strcmp(final String a, final String b) { final int result = a.compareTo(b); if (result < 0) { diff --git a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/model/AbstractModelLines.java b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/model/AbstractModelLines.java index 4827e4ea4..b4e5e5293 100644 --- a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/model/AbstractModelLines.java +++ b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/model/AbstractModelLines.java @@ -11,8 +11,8 @@ */ package org.eclipse.tm4e.core.model; +import java.util.ArrayList; import java.util.Collections; -import java.util.LinkedList; import java.util.List; import org.eclipse.jdt.annotation.Nullable; @@ -37,11 +37,18 @@ static final class ModelLine { List tokens = Collections.emptyList(); } - private final List list = new LinkedList<>(); + private final List list; @Nullable private TMModel model; + protected AbstractModelLines(final int initialNumberOfLines) { + list = new ArrayList<>(Math.max(10, initialNumberOfLines)); + for (int i = 0; i < initialNumberOfLines; i++) { + list.add(new ModelLine()); + } + } + void setModel(@Nullable final TMModel model) { this.model = model; synchronized (list) { diff --git a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/model/TMTokenization.java b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/model/TMTokenization.java index c7017d8fa..fddf914c1 100644 --- a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/model/TMTokenization.java +++ b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/model/TMTokenization.java @@ -29,8 +29,7 @@ import org.eclipse.tm4e.core.grammar.IGrammar; import org.eclipse.tm4e.core.grammar.IStateStack; import org.eclipse.tm4e.core.internal.grammar.StateStack; - -import com.google.common.base.Splitter; +import org.eclipse.tm4e.core.internal.utils.StringUtils; /** * @see scopeToTokenIds = new LinkedHashMap<>(); private final Map tokenToTokenId = new LinkedHashMap<>(); @@ -153,7 +150,7 @@ Integer[] getTokenIds(final String scope) { if (tokens != null) { return tokens; } - final String[] tmpTokens = BY_DOT_SPLITTER.splitToStream(scope).toArray(String[]::new); + final String[] tmpTokens = StringUtils.splitToArray(scope, '.'); tokens = new Integer[tmpTokens.length]; for (int i = 0; i < tmpTokens.length; i++) { diff --git a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/theme/css/CSSParser.java b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/theme/css/CSSParser.java index 20f22d7f1..39ff457b0 100644 --- a/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/theme/css/CSSParser.java +++ b/org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/theme/css/CSSParser.java @@ -27,7 +27,6 @@ import org.w3c.css.sac.CSSException; import org.w3c.css.sac.InputSource; import org.w3c.css.sac.Parser; -import org.w3c.css.sac.Selector; import org.w3c.css.sac.SelectorList; /** @@ -35,7 +34,10 @@ */ public class CSSParser { - private final CSSDocumentHandler handler; + protected final CSSDocumentHandler handler = new CSSDocumentHandler(); + + protected CSSParser() { + } public CSSParser(final InputStream source) throws Exception { this(toSource(source)); @@ -56,7 +58,6 @@ public CSSParser(final String source) throws Exception { } public CSSParser(final InputSource source, final Parser parser) throws CSSException, IOException { - this.handler = new CSSDocumentHandler(); parser.setDocumentHandler(handler); parser.setConditionFactory(CSSConditionFactory.INSTANCE); parser.setSelectorFactory(CSSSelectorFactory.INSTANCE); @@ -69,15 +70,13 @@ public IStyle getBestStyle(final String... names) { IStyle bestStyle = null; for (final IStyle style : handler.getList()) { final SelectorList list = ((CSSStyle) style).getSelectorList(); - for (int i = 0; i < list.getLength(); i++) { - final Selector selector = list.item(i); - if (selector instanceof final ExtendedSelector s) { - final int nbMatch = s.nbMatch(names); - if ((nbMatch >= bestSpecificity || bestStyle == null) - && nbMatch > 0 && nbMatch == s.nbClass()) { - bestStyle = style; - bestSpecificity = nbMatch; - } + for (int i = 0, l = list.getLength(); i < l; i++) { + final var selector = (ExtendedSelector) list.item(i); + final int nbMatch = selector.nbMatch(names); + if ((nbMatch >= bestSpecificity || bestStyle == null) + && nbMatch > 0 && nbMatch == selector.nbClass()) { + bestStyle = style; + bestSpecificity = nbMatch; } } } @@ -87,5 +86,4 @@ public IStyle getBestStyle(final String... names) { public List getStyles() { return handler.getList(); } - } diff --git a/org.eclipse.tm4e.core/src/test/java/org/eclipse/tm4e/core/internal/utils/StringUtilsTest.java b/org.eclipse.tm4e.core/src/test/java/org/eclipse/tm4e/core/internal/utils/StringUtilsTest.java new file mode 100644 index 000000000..10b995c87 --- /dev/null +++ b/org.eclipse.tm4e.core/src/test/java/org/eclipse/tm4e/core/internal/utils/StringUtilsTest.java @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2023 Vegard IT GmbH and others. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Sebastian Thomschke - initial implementation + */ +package org.eclipse.tm4e.core.internal.utils; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; + +import org.junit.jupiter.api.Test; + +class StringUtilsTest { + + @Test + void testSplitToArray() { + assertArrayEquals(new String[] { "" }, StringUtils.splitToArray("", '.')); + assertArrayEquals(new String[] { "abc" }, StringUtils.splitToArray("abc", '.')); + assertArrayEquals(new String[] { "abc", "" }, StringUtils.splitToArray("abc.", '.')); + assertArrayEquals(new String[] { "", "abc", "" }, StringUtils.splitToArray(".abc.", '.')); + assertArrayEquals(new String[] { "", "" }, StringUtils.splitToArray(".", '.')); + assertArrayEquals(new String[] { "", "", "", "" }, StringUtils.splitToArray("...", '.')); + assertArrayEquals(new String[] { "1", "2", "3", "4", "5", "6", "7", "8" }, + StringUtils.splitToArray("1.2.3.4.5.6.7.8", '.')); + + // test internal array resize + assertArrayEquals(new String[] { "1", "2", "3", "4", "5", "6", "7", "8", "9" }, + StringUtils.splitToArray("1.2.3.4.5.6.7.8.9", '.')); + } + + @Test + void testSplitToList() { + assertEquals(List.of(""), StringUtils.splitToList("", '.')); + assertEquals(List.of("abc"), StringUtils.splitToList("abc", '.')); + assertEquals(List.of("abc", ""), StringUtils.splitToList("abc.", '.')); + assertEquals(List.of("", "abc", ""), StringUtils.splitToList(".abc.", '.')); + assertEquals(List.of("", ""), StringUtils.splitToList(".", '.')); + assertEquals(List.of("", "", "", ""), StringUtils.splitToList("...", '.')); + assertEquals(List.of("1", "2", "3", "4", "5", "6", "7", "8"), + StringUtils.splitToList("1.2.3.4.5.6.7.8", '.')); + + // test internal array resize + assertEquals(List.of("1", "2", "3", "4", "5", "6", "7", "8", "9"), + StringUtils.splitToList("1.2.3.4.5.6.7.8.9", '.')); + } +} diff --git a/org.eclipse.tm4e.core/src/test/java/org/eclipse/tm4e/core/model/TMModelTest.java b/org.eclipse.tm4e.core/src/test/java/org/eclipse/tm4e/core/model/TMModelTest.java index 583db0558..b9d04b861 100644 --- a/org.eclipse.tm4e.core/src/test/java/org/eclipse/tm4e/core/model/TMModelTest.java +++ b/org.eclipse.tm4e.core/src/test/java/org/eclipse/tm4e/core/model/TMModelTest.java @@ -31,7 +31,7 @@ function addNumbers(a: number, b: number) { // 1 """.split("\\r?\\n"); assertEquals(5, lines.length); - final var modelLines = new AbstractModelLines() { + final var modelLines = new AbstractModelLines(0) { @Override public String getLineText(final int lineIndex) throws Exception { return lines[lineIndex]; diff --git a/org.eclipse.tm4e.core/src/test/java/org/eclipse/tm4e/core/theme/css/CSSParserTest.java b/org.eclipse.tm4e.core/src/test/java/org/eclipse/tm4e/core/theme/css/CSSParserTest.java index 3e6560ac4..e8f13e08a 100644 --- a/org.eclipse.tm4e.core/src/test/java/org/eclipse/tm4e/core/theme/css/CSSParserTest.java +++ b/org.eclipse.tm4e.core/src/test/java/org/eclipse/tm4e/core/theme/css/CSSParserTest.java @@ -11,17 +11,16 @@ */ package org.eclipse.tm4e.core.theme.css; +import org.eclipse.tm4e.core.internal.utils.StringUtils; import org.eclipse.tm4e.core.theme.IStyle; public final class CSSParserTest { public static void main(final String[] args) throws Exception { final var parser = new CSSParser(".comment {color:rgb(0,1,2)} .comment.ts {color:rgb(0,1,2)}"); - String[] names = "comment".split("[.]"); - parser.getBestStyle(names); + parser.getBestStyle("comment"); - names = "comment.ts".split("[.]"); - final IStyle style = parser.getBestStyle(names); + final IStyle style = parser.getBestStyle(StringUtils.splitToArray("comment.ts", '.')); System.err.println(style.getColor().red); } diff --git a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/model/DocumentModelLines.java b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/model/DocumentModelLines.java index a7f9d730b..cb9964873 100644 --- a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/model/DocumentModelLines.java +++ b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/model/DocumentModelLines.java @@ -30,9 +30,9 @@ final class DocumentModelLines extends AbstractModelLines implements IDocumentLi private int endLineIndexOfRemovedText = -1; DocumentModelLines(final IDocument document) { + super(document.getNumberOfLines()); this.document = document; document.addDocumentListener(this); - addLines(0, document.getNumberOfLines()); } @Override diff --git a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/themes/AbstractThemeManager.java b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/themes/AbstractThemeManager.java index 6eff6c730..bb3f8f1d8 100644 --- a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/themes/AbstractThemeManager.java +++ b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/themes/AbstractThemeManager.java @@ -138,7 +138,7 @@ public IThemeAssociation[] getAllThemeAssociations() { } @Override - public ITokenProvider getThemeForScope(final String scopeName, final RGB background) { + public ITheme getThemeForScope(final String scopeName, final RGB background) { return getThemeForScope(scopeName, 0.299 * background.red + 0.587 * background.green + 0.114 * background.blue < 128); diff --git a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/text/TMPresentationReconciler.java b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/text/TMPresentationReconciler.java index 1cfc1f6b4..793a5fbb0 100644 --- a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/text/TMPresentationReconciler.java +++ b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/text/TMPresentationReconciler.java @@ -90,9 +90,6 @@ */ public class TMPresentationReconciler implements IPresentationReconciler { - /** The default text attribute if none is returned as data by the current token. */ - private final Token defaultToken; - /** The target viewer. */ @Nullable private ITextViewer viewer; @@ -125,7 +122,6 @@ public class TMPresentationReconciler implements IPresentationReconciler { private boolean throwError; public TMPresentationReconciler() { - this.defaultToken = new Token(null); this.internalListener = new InternalListener(); this.fDefaultTextAttribute = new TextAttribute(null); if (PreferenceUtils.isDebugGenerateTest()) { @@ -528,6 +524,9 @@ private void colorize(final IRegion damage, final TMDocumentModel model) throws TMUIPlugin.logTrace("Render from: " + fromLineIndex + " to: " + toLineIndex); final var presentation = new TextPresentation(damage, 1000); Exception error = null; + + final var tokenProvider = this.tokenProvider; + try { int lastStart = presentation.getExtent().getOffset(); int length = 0; @@ -561,7 +560,8 @@ private void colorize(final IRegion damage, final TMDocumentModel model) throws tokenStartIndex = damage.getOffset() - startLineOffset; } else { tokenStartIndex = damage.getOffset() - startLineOffset; - final IToken token = toToken(currentToken); + final IToken token = tokenProvider == null ? ITokenProvider.DEFAULT_TOKEN + : tokenProvider.getToken(currentToken.type); lastAttribute = getTokenTextAttribute(token); length += getTokenLengh(tokenStartIndex, nextToken, lineIndex, doc); firstToken = false; @@ -573,7 +573,7 @@ private void colorize(final IRegion damage, final TMDocumentModel model) throws break; } - final IToken token = toToken(currentToken); + final IToken token = tokenProvider == null ? ITokenProvider.DEFAULT_TOKEN : tokenProvider.getToken(currentToken.type); final TextAttribute attribute = getTokenTextAttribute(token); if (lastAttribute.equals(attribute)) { length += getTokenLengh(tokenStartIndex, nextToken, lineIndex, doc); @@ -616,17 +616,6 @@ private boolean isAfterRegion(final TMToken token, final int startLineOffset, fi return token.startIndex + startLineOffset >= damage.getOffset() + damage.getLength(); } - private IToken toToken(final TMToken token) { - final var tokenProvider = this.tokenProvider; - if (tokenProvider != null) { - final IToken result = tokenProvider.getToken(token.type); - if (result != null) { - return result; - } - } - return defaultToken; - } - private int getTokenLengh(final int tokenStartIndex, @Nullable final TMToken nextToken, final int line, final IDocument document) throws BadLocationException { if (nextToken != null) { diff --git a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/themes/AbstractTokenProvider.java b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/themes/AbstractTokenProvider.java deleted file mode 100644 index 8a119512f..000000000 --- a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/themes/AbstractTokenProvider.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) 2015-2017 Angelo ZERR. - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * Angelo Zerr - initial API and implementation - */ -package org.eclipse.tm4e.ui.themes; - -/** - * Abstract class for {@link ITokenProvider}. - * - * TODO remove? - */ -public abstract class AbstractTokenProvider implements ITokenProvider { - -} diff --git a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/themes/ColorManager.java b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/themes/ColorManager.java index 1fdd15685..33f097f94 100644 --- a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/themes/ColorManager.java +++ b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/themes/ColorManager.java @@ -17,41 +17,34 @@ import org.eclipse.jdt.annotation.Nullable; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.widgets.Display; +import org.eclipse.tm4e.core.internal.utils.StringUtils; import org.eclipse.tm4e.core.theme.RGB; import org.eclipse.tm4e.ui.internal.utils.PreferenceUtils; import org.eclipse.ui.texteditor.AbstractTextEditor; -import com.google.common.base.Splitter; - public final class ColorManager { - private static final Splitter BY_COMMA_SPLITTER = Splitter.on(','); - private static final ColorManager INSTANCE = new ColorManager(); public static ColorManager getInstance() { return INSTANCE; } - private final Map fColorTable = new HashMap<>(10); + private static Color rgbToColor(RGB rgb) { + return new Color(Display.getCurrent(), rgb.red, rgb.green, rgb.blue); + } + + private final Map fColorTable = new HashMap<>(10); private ColorManager() { } public Color getColor(final RGB rgb) { - Color color = fColorTable.get(rgb); - if (color == null) { - color = new Color(Display.getCurrent(), rgb.red, rgb.green, rgb.blue); - fColorTable.put(rgb, color); - } - return color; + return fColorTable.computeIfAbsent(rgb, ColorManager::rgbToColor); } public void dispose() { - for (final var c : fColorTable.values()) { - if (c != null) - c.dispose(); - } + fColorTable.values().forEach(Color::dispose); } /** @@ -100,8 +93,7 @@ public boolean isColorUserDefined(final String tokenId) { @Nullable public Color getPriorityColor(@Nullable final Color themeColor, final String tokenId) { if (isColorUserDefined(tokenId)) { - final Color prefColor = getPreferenceEditorColor(tokenId); - return prefColor; + return getPreferenceEditorColor(tokenId); } return themeColor != null ? themeColor : null; @@ -132,10 +124,9 @@ private String getSystemDefaultToken(final String tokenId) { * @return RGB value */ private RGB stringToRGB(final String value) { - final String[] rgbValues = BY_COMMA_SPLITTER.splitToStream(value).toArray(String[]::new); + final String[] rgbValues = StringUtils.splitToArray(value, ','); return rgbValues.length == 3 - ? new RGB(Integer.parseInt(rgbValues[0]), Integer.parseInt(rgbValues[1]), - Integer.parseInt(rgbValues[2])) + ? new RGB(Integer.parseInt(rgbValues[0]), Integer.parseInt(rgbValues[1]), Integer.parseInt(rgbValues[2])) : new RGB(255, 255, 255); } } diff --git a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/themes/IThemeManager.java b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/themes/IThemeManager.java index 23b53d929..5a5dbba6e 100644 --- a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/themes/IThemeManager.java +++ b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/themes/IThemeManager.java @@ -138,5 +138,5 @@ public interface IThemeManager { * * @return the theme that will fit best for the defined background color */ - ITokenProvider getThemeForScope(String scopeName, RGB background); + ITheme getThemeForScope(String scopeName, RGB background); } diff --git a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/themes/ITokenProvider.java b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/themes/ITokenProvider.java index a68f5080c..0c84a62b0 100644 --- a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/themes/ITokenProvider.java +++ b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/themes/ITokenProvider.java @@ -13,6 +13,7 @@ import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.rules.IToken; +import org.eclipse.jface.text.rules.Token; import org.eclipse.swt.graphics.Color; /** @@ -21,14 +22,15 @@ */ public interface ITokenProvider { + IToken DEFAULT_TOKEN = new Token(null); + /** - * Returns the Eclipse {@link IToken} from the given type and null otherwise. + * Returns the Eclipse {@link IToken} from the given type and {@link #DEFAULT_TOKEN} otherwise. * * @param type - * - * @return the Eclipse {@link IToken} from the given type and null otherwise. + * + * @return the Eclipse {@link IToken} from the given type and {@link #DEFAULT_TOKEN} otherwise. */ - @Nullable IToken getToken(String type); @Nullable diff --git a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/themes/Theme.java b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/themes/Theme.java index 65400957d..1ecb351eb 100644 --- a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/themes/Theme.java +++ b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/themes/Theme.java @@ -81,11 +81,10 @@ public String getName() { return name; } - @Nullable @Override public IToken getToken(final String type) { final ITokenProvider provider = getTokenProvider(); - return provider != null ? provider.getToken(type) : null; + return provider == null ? ITokenProvider.DEFAULT_TOKEN : provider.getToken(type); } @Nullable diff --git a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/themes/css/CSSTokenProvider.java b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/themes/css/CSSTokenProvider.java index d0ddeb28c..5ce3aefe1 100644 --- a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/themes/css/CSSTokenProvider.java +++ b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/themes/css/CSSTokenProvider.java @@ -12,8 +12,11 @@ package org.eclipse.tm4e.ui.themes.css; import java.io.InputStream; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.TextAttribute; @@ -21,25 +24,35 @@ import org.eclipse.jface.text.rules.Token; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; +import org.eclipse.tm4e.core.internal.utils.StringUtils; import org.eclipse.tm4e.core.theme.IStyle; import org.eclipse.tm4e.core.theme.RGB; import org.eclipse.tm4e.core.theme.css.CSSParser; import org.eclipse.tm4e.ui.TMUIPlugin; -import org.eclipse.tm4e.ui.themes.AbstractTokenProvider; import org.eclipse.tm4e.ui.themes.ColorManager; +import org.eclipse.tm4e.ui.themes.ITokenProvider; -import com.google.common.base.Splitter; +public class CSSTokenProvider implements ITokenProvider { -public class CSSTokenProvider extends AbstractTokenProvider { + private static class NoopCSSParser extends CSSParser { + @Override + public List getStyles() { + return Collections.emptyList(); + } - private static final Splitter BY_DOT_SPLITTER = Splitter.on('.'); + @Override + public @Nullable IStyle getBestStyle(String... names) { + return null; + } + } private final Map tokenMaps = new HashMap<>(); + private final Map getTokenReturnValueCache = new ConcurrentHashMap<>(); - @Nullable - private CSSParser parser; + private final CSSParser parser; public CSSTokenProvider(final InputStream in) { + CSSParser parser = null; try { parser = new CSSParser(in); for (final IStyle style : parser.getStyles()) { @@ -65,31 +78,28 @@ public CSSTokenProvider(final InputStream in) { } catch (final Exception ex) { TMUIPlugin.logError(ex); } + + this.parser = parser == null ? new NoopCSSParser() : parser; } - @Nullable @Override public IToken getToken(@Nullable final String type) { - if (type == null) - return null; + if (type == null || type.isEmpty()) + return DEFAULT_TOKEN; - final var parser = this.parser; - if (parser == null) - return null; + return getTokenReturnValueCache.computeIfAbsent(type, this::getTokenInternal); + } - final IStyle style = parser.getBestStyle(BY_DOT_SPLITTER.splitToStream(type).toArray(String[]::new)); + private IToken getTokenInternal(final String type) { + final IStyle style = parser.getBestStyle(StringUtils.splitToArray(type, '.')); if (style == null) - return null; - - return tokenMaps.get(style); + return DEFAULT_TOKEN; + final IToken token = tokenMaps.get(style); + return token == null ? DEFAULT_TOKEN : token; } @Nullable private Color getColor(final boolean isForeground, final String... styles) { - final var parser = this.parser; - if (parser == null) - return null; - final var style = parser.getBestStyle(styles); if (style == null) return null; @@ -123,7 +133,6 @@ public Color getEditorSelectionForeground() { @Override public Color getEditorSelectionBackground() { return getColor(false, "editor", "selection"); - } @Nullable