From e34e2f5a9813abde2b84ce0b0054775a655c3ac3 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 20 Apr 2012 20:03:01 +0200 Subject: [PATCH] IDEA-84485 Allow auto-completion by typing the middle of a word (hidden for now) --- .../completion/NormalCompletionTest.groovy | 4 +-- .../completion/PrefixMatchingWeigher.java | 3 +- .../completion/impl/CamelHumpMatcher.java | 29 +++++++++++++------ .../src/misc/registry.properties | 3 ++ .../com/intellij/psi/util/NameUtilTest.java | 5 ++++ .../com/intellij/psi/codeStyle/NameUtil.java | 7 +++-- 6 files changed, 37 insertions(+), 14 deletions(-) diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy index 6567b64cd193..fcf883508374 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy @@ -942,8 +942,8 @@ public class ListUtils { assertNotNull(getLookup()); type('*fz'); final List list = getLookup().getItems(); - assertEquals("azzzfzzz", list.get(0).getLookupString()); - assertEquals("fzazzz", list.get(1).getLookupString()); + assertEquals("azzzfzzz", list.get(1).getLookupString()); + assertEquals("fzazzz", list.get(0).getLookupString()); } public void testTabReplacesMethodNameWithLocalVariableName() throws Throwable { doTest('\t'); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/PrefixMatchingWeigher.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/PrefixMatchingWeigher.java index a688fc1157df..5290bf1e5ee3 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/PrefixMatchingWeigher.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/PrefixMatchingWeigher.java @@ -16,6 +16,7 @@ package com.intellij.codeInsight.completion; import com.intellij.codeInsight.CodeInsightSettings; +import com.intellij.codeInsight.completion.impl.CamelHumpMatcher; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.psi.codeStyle.NameUtil; import org.jetbrains.annotations.NotNull; @@ -37,7 +38,7 @@ public class PrefixMatchingWeigher extends CompletionWeigher { final NameUtil.MatchingCaseSensitivity sensitivity = setting == CodeInsightSettings.NONE ? NameUtil.MatchingCaseSensitivity.NONE : setting == CodeInsightSettings.FIRST_LETTER ? NameUtil.MatchingCaseSensitivity.FIRST_LETTER : NameUtil.MatchingCaseSensitivity.ALL; - final NameUtil.MinusculeMatcher matcher = new NameUtil.MinusculeMatcher(prefix, sensitivity); + final NameUtil.MinusculeMatcher matcher = new NameUtil.MinusculeMatcher(CamelHumpMatcher.applyMiddleMatching(prefix), sensitivity); int max = Integer.MIN_VALUE; for (String lookupString : item.getAllLookupStrings()) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/impl/CamelHumpMatcher.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/impl/CamelHumpMatcher.java index 392d5195c461..7fa868148394 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/impl/CamelHumpMatcher.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/impl/CamelHumpMatcher.java @@ -4,6 +4,8 @@ package com.intellij.codeInsight.completion.impl; import com.intellij.codeInsight.CodeInsightSettings; import com.intellij.codeInsight.completion.PrefixMatcher; import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.codeStyle.NameUtil; import com.intellij.util.containers.hash.LinkedHashMap; @@ -64,7 +66,7 @@ public class CamelHumpMatcher extends PrefixMatcher { String key = relax + myPrefix + caseSensitive; Matcher pattern = ourPatternCache.get(key); if (pattern == null) { - pattern = createCamelHumpsMatcher(relax, caseSensitive); + pattern = createCamelHumpsMatcher(relax, caseSensitive, myPrefix); ourPatternCache.put(key, pattern); } return pattern; @@ -102,28 +104,37 @@ public class CamelHumpMatcher extends PrefixMatcher { return new CamelHumpMatcher(prefix, myCaseSensitive, myRelaxedMatching); } - private Matcher createCamelHumpsMatcher(final boolean relaxedMatching, final boolean caseSensitive) { + private static Matcher createCamelHumpsMatcher(final boolean relaxedMatching, final boolean caseSensitive, String prefix) { + prefix = applyMiddleMatching(prefix); + if (!caseSensitive) { - return NameUtil.buildCompletionMatcher(myPrefix, 0, true, true); + return NameUtil.buildCompletionMatcher(prefix, 0, true, true); } if (relaxedMatching) { - return NameUtil.buildCompletionMatcher(myPrefix, 0, true, true); + return NameUtil.buildCompletionMatcher(prefix, 0, true, true); } switch (CodeInsightSettings.getInstance().COMPLETION_CASE_SENSITIVE) { case CodeInsightSettings.NONE: - return NameUtil.buildCompletionMatcher(myPrefix, 0, true, true); + return NameUtil.buildCompletionMatcher(prefix, 0, true, true); case CodeInsightSettings.FIRST_LETTER: - int exactPrefixLen = myPrefix.startsWith("*") ? 0 : 1; - return NameUtil.buildCompletionMatcher(myPrefix, exactPrefixLen, true, true); + int exactPrefixLen = prefix.startsWith("*") ? 0 : 1; + return NameUtil.buildCompletionMatcher(prefix, exactPrefixLen, true, true); case CodeInsightSettings.ALL: - return NameUtil.buildCompletionMatcher(myPrefix, 1, false, false); + return NameUtil.buildCompletionMatcher(prefix, 1, false, false); default: - return NameUtil.buildCompletionMatcher(myPrefix, 0, true, false); + return NameUtil.buildCompletionMatcher(prefix, 0, true, false); } } + public static String applyMiddleMatching(String prefix) { + if (Registry.is("ide.completion.middle.matching") && !ApplicationManager.getApplication().isUnitTestMode()) { + return " " + prefix; + } + return prefix; + } + @Override public String toString() { return myPrefix; diff --git a/platform/platform-resources-en/src/misc/registry.properties b/platform/platform-resources-en/src/misc/registry.properties index 2a0dc42366f9..0a10aeccb1f9 100644 --- a/platform/platform-resources-en/src/misc/registry.properties +++ b/platform/platform-resources-en/src/misc/registry.properties @@ -181,6 +181,9 @@ projectView.hide.dot.idea=true show.live.templates.in.completion=false documentation.component.editor.font=false +ide.completion.middle.matching=false +ide.completion.middle.matching.description=Suggest items in completion that contain the entered string somewhere in the middle + show.all.classes.on.first.completion=false ide.enable.toolwindow.stack=false diff --git a/platform/platform-tests/testSrc/com/intellij/psi/util/NameUtilTest.java b/platform/platform-tests/testSrc/com/intellij/psi/util/NameUtilTest.java index a5b8318e2938..220338b31cff 100644 --- a/platform/platform-tests/testSrc/com/intellij/psi/util/NameUtilTest.java +++ b/platform/platform-tests/testSrc/com/intellij/psi/util/NameUtilTest.java @@ -119,6 +119,8 @@ public class NameUtilTest extends UsefulTestCase { assertMatches("foo bar", "fooGooBar"); assertMatches("foo bar", "fooGoo bar"); assertDoesntMatch(" b", "fbi"); + assertTrue(NameUtil.buildCompletionMatcher(" us", 0, true, true).matches("getUsage")); + assertTrue(NameUtil.buildCompletionMatcher(" us", 0, true, true).matches("getMyUsage")); } public void testIDEADEV15503() throws Exception { @@ -211,6 +213,8 @@ public class NameUtilTest extends UsefulTestCase { assertMatches("*Git*", "AtpGenerationItem"); assertMatches("Collec*Util*", "CollectionUtils"); assertMatches("Collec*Util*", "CollectionUtilsTest"); + assertTrue(NameUtil.buildCompletionMatcher("*us", 0, true, true).matches("usage")); + assertTrue(NameUtil.buildCompletionMatcher(" us", 0, true, true).matches("usage")); } public void testSpaceInCompletionPrefix() throws Exception { @@ -358,6 +362,7 @@ public class NameUtilTest extends UsefulTestCase { assertPreference("*f", "barfoo", "barFoo"); assertPreference("*f", "barfoo", "foo"); assertPreference("*f", "asdf", "Foo", NameUtil.MatchingCaseSensitivity.NONE); + assertPreference(" sto", "ArrayStoreException", "StackOverflowError", NameUtil.MatchingCaseSensitivity.NONE); } private static void assertPreference(@NonNls String pattern, diff --git a/platform/util/src/com/intellij/psi/codeStyle/NameUtil.java b/platform/util/src/com/intellij/psi/codeStyle/NameUtil.java index 87284ca0555f..932920caa364 100644 --- a/platform/util/src/com/intellij/psi/codeStyle/NameUtil.java +++ b/platform/util/src/com/intellij/psi/codeStyle/NameUtil.java @@ -335,6 +335,9 @@ public class NameUtil { if (isWordStart(text.charAt(i))) { return true; } + if (!Character.isLetterOrDigit(text.charAt(i))) { + return false; + } if (i > 0 && MinusculeMatcher.isWordSeparator(text.charAt(i - 1))) { return true; } @@ -600,7 +603,7 @@ public class NameUtil { } } - if (isWordStart(name, nameIndex)) { + if (nameIndex == 0 || isWordStart(name, nameIndex)) { FList ranges = matchName(name, patternIndex, nameIndex); if (ranges != null) { return ranges; @@ -657,7 +660,7 @@ public class NameUtil { boolean prefixMatching = first != null && first.getStartOffset() == 0; boolean middleWordStart = first != null && first.getStartOffset() > 0 && isWordStart(name, first.getStartOffset()); - return -fragmentCount + matchingCaps * 10 + commonStart * 2 + (prefixMatching ? 2 : middleWordStart ? 1 : 0); + return -fragmentCount + matchingCaps * 10 + commonStart + (prefixMatching ? 2 : middleWordStart ? 1 : 0) * 100; } @Override