IDEA-87938 Completion: extend 'middle-matching' by '*word*parts*' in completion

This commit is contained in:
peter
2012-07-25 16:58:01 +02:00
parent ce36c39d5b
commit 8a7c395e32
10 changed files with 599 additions and 445 deletions
@@ -1,3 +1,5 @@
-Camel.java
-Camel
[getSomeHead():void]
[doSomethingWithMyHead():void]
getSomeArtHeader():void
getSomeHead():void
@@ -80,7 +80,11 @@ public class SecondSmartTypeCompletionTest extends LightCompletionTestCase {
}
public void testNoObjectMethodsAsFirstPart() throws Throwable { doTest(); }
public void testGetClassLoader() throws Throwable { doTest(); }
public void testGetClassLoader() throws Throwable {
configure();
selectItem(myItems[0]);
checkResult();
}
public void testNewStaticProblem() throws Throwable { doTest(); }
public void testChainingPerformance() throws Throwable {
@@ -51,6 +51,11 @@ public class NameUtilTest extends UsefulTestCase {
assertMatches("AACl", "AAClass");
assertMatches("ZZZ", "ZZZZZZZZZZ");
}
public void testSkipWords() {
assertMatches("nt", "NameUtilTest");
assertDoesntMatch("ABCD", "AbstractButton.DISABLED_ICON_CHANGED_PROPERTY");
}
public void testSimpleCasesWithFirstLowercased() throws Exception {
assertMatches("N", "nameUtilTest");
@@ -79,9 +84,9 @@ public class NameUtilTest extends UsefulTestCase {
public void testXMLCompletion() throws Exception {
assertDoesntMatch("N_T", "NameUtilTest");
assertDoesntMatch("ORGS_ACC", "ORGS_POSITION_ACCOUNTABILITY");
assertDoesntMatch("ORGS-ACC", "ORGS-POSITION_ACCOUNTABILITY");
assertDoesntMatch("ORGS.ACC", "ORGS.POSITION_ACCOUNTABILITY");
assertMatches("ORGS_ACC", "ORGS_POSITION_ACCOUNTABILITY");
assertMatches("ORGS-ACC", "ORGS-POSITION_ACCOUNTABILITY");
assertMatches("ORGS.ACC", "ORGS.POSITION_ACCOUNTABILITY");
}
public void testStarFalsePositive() throws Exception {
@@ -112,7 +117,7 @@ public class NameUtilTest extends UsefulTestCase {
assertDoesntMatch("ARS.j", "activity_report_summary.xml");
assertDoesntMatch("ARS.j", "activity_report_summary_justsometingwrong.xml");
assertDoesntMatch("foo.goo", "foo.bar.goo");
assertMatches("foo.goo", "foo.bar.goo");
}
public void testSpaceForAnyWordsInBetween() {
@@ -168,8 +173,8 @@ public class NameUtilTest extends UsefulTestCase {
public void testSkipDot() {
assertMatches("ja", "jquery.autocomplete.js");
assertMatches("ja.js", "jquery.autocomplete.js");
assertDoesntMatch("jajs", "jquery.autocomplete.js");
assertDoesntMatch("j.ajs", "jquery.autocomplete.js");
assertMatches("jajs", "jquery.autocomplete.js");
assertMatches("j.ajs", "jquery.autocomplete.js");
}
public void testNoExtension() {
@@ -192,11 +197,11 @@ public class NameUtilTest extends UsefulTestCase {
assertDoesntMatch("*inspection*.pro", "InspectionsInProgress.png");
}
public void testIgnoreLeadingUnderscore() throws Exception {
assertMatches("form", "_form.html.erb");
public void testLeadingUnderscore() throws Exception {
assertDoesntMatch("form", "_form.html.erb");
assertMatches("_form", "_form.html.erb");
assertMatches("_form", "__form");
assertFalse(NameUtil.buildMatcher("_form", 1, true, true, false).matches("__form"));
assertTrue(firstLetterMatcher("_form").matches("__form"));
}
public void testLowerCaseWords() throws Exception {
@@ -208,6 +213,8 @@ public class NameUtilTest extends UsefulTestCase {
}
public void testObjectiveCCases() throws Exception {
assertMatches("h*:", "h:aaa");
assertMatches("h:", "h:aaa");
assertMatches("text:sh", "textField:shouldChangeCharactersInRange:replacementString:");
assertMatches("abc", "aaa:bbb:ccc");
assertMatches("textField:sh", "textField:shouldChangeCharactersInRange:replacementString:");
@@ -293,7 +300,7 @@ public class NameUtilTest extends UsefulTestCase {
assertDoesntMatch("foo", "fxoo");
assertMatches("foo", "fOo");
assertMatches("foo", "fxOo");
assertDoesntMatch("foo", "fXOo");
assertMatches("foo", "fXOo");
assertMatches("fOo", "foo");
assertMatches("fOo", "FaOaOaXXXX");
assertMatches("ncdfoe", "NoClassDefFoundException");
@@ -338,7 +345,7 @@ public class NameUtilTest extends UsefulTestCase {
assertMatches("a@b", "a@bc");
assertMatches("a/text", "a/Text");
assertDoesntMatch("a/text", "a/bbbText");
assertMatches("a/text", "a/bbbText");
}
public void testMinusculeFirstLetter() {
@@ -459,7 +466,7 @@ public class NameUtilTest extends UsefulTestCase {
for (String s : CollectionFactory.ar("*", "*i", "*a", "*u", "T", "ti", longName, longName.substring(0, 20))) {
matching.add(new MinusculeMatcher(s, NameUtil.MatchingCaseSensitivity.NONE));
}
for (String s : CollectionFactory.ar("A", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "ta")) {
for (String s : CollectionFactory.ar("A", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "tag")) {
nonMatching.add(new MinusculeMatcher(s, NameUtil.MatchingCaseSensitivity.NONE));
}
@@ -26,6 +26,8 @@ import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.Iterator;
import static com.intellij.psi.codeStyle.NameUtil.MatchingCaseSensitivity.*;
/**
* @author peter
*/
@@ -35,7 +37,7 @@ public class MinusculeMatcher implements Matcher {
public MinusculeMatcher(String pattern, NameUtil.MatchingCaseSensitivity options) {
myOptions = options;
myPattern = StringUtil.trimEnd(pattern, "* ").replaceAll(":", "\\*:").toCharArray();
myPattern = StringUtil.trimEnd(pattern, "* ").toCharArray();
}
@Nullable
@@ -74,7 +76,7 @@ public class MinusculeMatcher implements Matcher {
return null;
}
if (myOptions == NameUtil.MatchingCaseSensitivity.ALL && name.charAt(nameIndex) != myPattern[patternIndex]) {
if (myOptions == ALL && name.charAt(nameIndex) != myPattern[patternIndex]) {
return null;
}
@@ -89,14 +91,14 @@ public class MinusculeMatcher implements Matcher {
}
char p = myPattern[patternIndex + i];
char w = name.charAt(i + nameIndex);
if (lastUpper == i - 1 && NameUtil.isWordStart(p) && myOptions != NameUtil.MatchingCaseSensitivity.ALL) {
if (lastUpper == i - 1 && NameUtil.isWordStart(p) && myOptions != ALL) {
if (p == w) {
lastUpper = i;
}
p = StringUtil.toLowerCase(p);
}
if (myOptions != NameUtil.MatchingCaseSensitivity.ALL) {
if (myOptions != ALL) {
w = StringUtil.toLowerCase(w);
}
if (w != p) {
@@ -122,8 +124,10 @@ public class MinusculeMatcher implements Matcher {
@Nullable
private FList<TextRange> matchAfterFragment(String name, int patternIndex, int nameIndex, int nextStart, int lastUpper, int matchLen) {
boolean star = patternIndex + matchLen < myPattern.length && myPattern[patternIndex + matchLen] == '*';
if (lastUpper >= 0) {
FList<TextRange> ranges = matchName(name, patternIndex + lastUpper + 1, star && matchLen == lastUpper ? nameIndex + lastUpper : nextStart);
if (lastUpper >= 1) {
int nextNameIndex = star && matchLen == lastUpper ? nameIndex + lastUpper : nextStart;
int nextPatternIndex = patternIndex + lastUpper + 1;
FList<TextRange> ranges = star ? matchName(name, nextPatternIndex, nextNameIndex) : skipWords(name, nextPatternIndex, nextNameIndex);
if (ranges != null) {
return prependRange(ranges, nameIndex, lastUpper + 1);
}
@@ -131,7 +135,7 @@ public class MinusculeMatcher implements Matcher {
int trial = matchLen;
while (trial > 0) {
FList<TextRange> ranges = matchName(name, patternIndex + trial, nextStart);
FList<TextRange> ranges = skipWords(name, patternIndex + trial, nextStart);
if (ranges != null) {
return prependRange(ranges, nameIndex, trial);
}
@@ -230,11 +234,17 @@ public class MinusculeMatcher implements Matcher {
@Nullable
private FList<TextRange> skipWords(String name, int patternIndex, int nameIndex) {
while (' ' == myPattern[patternIndex]) {
while (patternIndex < myPattern.length && ' ' == myPattern[patternIndex]) {
patternIndex++;
if (patternIndex == myPattern.length) {
return null;
}
}
if (patternIndex == myPattern.length) {
return FList.emptyList();
}
if (nameIndex == name.length()) {
return null;
}
if (myPattern[patternIndex] == '*') {
return skipChars(name, patternIndex, nameIndex, true);
}
if (nameIndex == 0 || NameUtil.isWordStart(name, nameIndex)) {
@@ -244,7 +254,7 @@ public class MinusculeMatcher implements Matcher {
}
}
boolean separatorInPattern = NameUtil.isWordSeparator(myPattern[patternIndex]);
boolean separatorInPattern = !Character.isLetterOrDigit(myPattern[patternIndex]);
int fromIndex = nameIndex;
while (fromIndex < name.length()) {
int next = separatorInPattern ? name.indexOf(myPattern[patternIndex], fromIndex) : NameUtil.nextWord(name, fromIndex);
@@ -340,9 +350,124 @@ public class MinusculeMatcher implements Matcher {
return myPattern.length == 0 ? Collections.<TextRange>emptyList() : null;
}
if (true) {
return matchWildcards(name, 0, 0);
}
return matchName(name, 0, 0);
}
@Nullable
private FList<TextRange> matchWildcards(String name, int patternIndex, int nameIndex) {
while (isWildcard(patternIndex)) {
patternIndex++;
}
boolean space = isPatternChar(patternIndex - 1, ' ');
if (patternIndex == myPattern.length) {
if (space && nameIndex != name.length() && (patternIndex < 2 || !NameUtil.isWordStart(myPattern[patternIndex - 2]))) {
return null;
}
return FList.emptyList();
}
char p = myPattern[patternIndex];
if (isWildcard(patternIndex - 1)) {
{
FList<TextRange> ranges = matchFragment(name, patternIndex, nameIndex);
if (ranges != null) {
return ranges;
}
}
while (true) {
nameIndex = space ? indexOfWordStart(name, p, nameIndex) : StringUtil.indexOfIgnoreCase(name, p, nameIndex + 1);
if (nameIndex < 0) return null;
if (!Character.isUpperCase(p) || NameUtil.isWordStart(name, nameIndex)) {
FList<TextRange> ranges = matchFragment(name, patternIndex, nameIndex);
if (ranges != null) {
return ranges;
}
}
}
}
return matchFragment(name, patternIndex, nameIndex);
}
@Nullable
private FList<TextRange> matchFragment(String name, int patternIndex, int nameIndex) {
if (!isFirstCharMatching(name, nameIndex, myPattern[patternIndex])) {
return null;
}
int minFragment = isPatternChar(patternIndex - 1, '*') && Character.isLetterOrDigit(name.charAt(nameIndex)) && !NameUtil.isWordStart(name, nameIndex) ? 2 : 1;
int i = 1;
while (nameIndex + i < name.length() &&
patternIndex + i < myPattern.length &&
StringUtil.charsEqual(myPattern[patternIndex + i], name.charAt(nameIndex + i), myOptions != ALL)) {
if (Character.isUpperCase(myPattern[patternIndex + i])) {
if (i < minFragment) {
return null;
}
if (myPattern[patternIndex + i] != name.charAt(nameIndex + i)) {
int nextWordStart = indexOfWordStart(name, myPattern[patternIndex + i], nameIndex + i);
if (nextWordStart >= 0) {
FList<TextRange> ranges = matchWildcards(name, patternIndex + i, nextWordStart);
if (ranges != null) {
return prependRange(ranges, nameIndex, i);
}
}
}
}
i++;
}
if (patternIndex + i >= myPattern.length) {
return i >= minFragment ? FList.<TextRange>emptyList().prepend(TextRange.from(nameIndex, i)) : null;
}
while (i >= minFragment) {
int nextWordStart = isWildcard(patternIndex + i) ? nameIndex + i : indexOfWordStart(name, myPattern[patternIndex + i], nameIndex + i);
if (nextWordStart >= 0) {
FList<TextRange> ranges = matchWildcards(name, patternIndex + i, nextWordStart);
if (ranges != null) {
return prependRange(ranges, nameIndex, i);
}
}
i--;
}
return null;
}
private boolean isFirstCharMatching(String name, int nameIndex, char p) {
return nameIndex < name.length() && StringUtil.charsEqual(p, name.charAt(nameIndex),
myOptions == FIRST_LETTER && nameIndex > 0 || myOptions == NONE);
}
private boolean isWildcard(int patternIndex) {
return isPatternChar(patternIndex, ' ') || isPatternChar(patternIndex, '*');
}
private boolean isPatternChar(int patternIndex, char c) {
if (patternIndex < 0 || patternIndex >= myPattern.length) return false;
return myPattern[patternIndex] == c;
}
private static int indexOfWordStart(String name, char p, int startFrom) {
if (p == '.' || NameUtil.isWordSeparator(p)) {
return StringUtil.indexOfIgnoreCase(name, p, startFrom + 1);
}
if (startFrom >= name.length()) {
return -1;
}
int nextWordStart = startFrom;
while (true) {
nextWordStart = NameUtil.nextWord(name, nextWordStart);
if (nextWordStart >= name.length()) {
return -1;
}
if (StringUtil.charsEqualIgnoreCase(p, name.charAt(nextWordStart))) {
return nextWordStart;
}
}
}
@Override
public String toString() {
return "MinusculeMatcher{" +
@@ -1,415 +1,415 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.codeStyle;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.util.text.StringUtilRt;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public class NameUtil {
private static final Function<String,String> LOWERCASE_MAPPING = new Function<String, String>() {
public String fun(final String s) {
return s.toLowerCase();
}
};
private static final int MAX_LENGTH = 40;
private NameUtil() {}
public static List<String> nameToWordsLowerCase(String name){
return ContainerUtil.map(nameToWords(name), LOWERCASE_MAPPING);
}
public static String[] nameToWords(String name){
ArrayList<String> array = new ArrayList<String>();
int index = 0;
int wordStart;
while(index < name.length()){
wordStart = index;
int upperCaseCount = 0;
int lowerCaseCount = 0;
int digitCount = 0;
int specialCount = 0;
while(index < name.length()){
char c = name.charAt(index);
if (Character.isDigit(c)){
if (upperCaseCount > 0 || lowerCaseCount > 0 || specialCount > 0) break;
digitCount++;
}
else if (Character.isUpperCase(c)){
if (lowerCaseCount > 0 || digitCount > 0 || specialCount > 0) break;
upperCaseCount++;
}
else if (Character.isLowerCase(c)){
if (digitCount > 0 || specialCount > 0) break;
if (upperCaseCount > 1) {
index--;
break;
}
lowerCaseCount++;
}
else{
if (upperCaseCount > 0 || lowerCaseCount > 0 || digitCount > 0) break;
specialCount++;
}
index++;
}
String word = name.substring(wordStart, index);
if (!StringUtil.isEmptyOrSpaces(word)) {
array.add(word);
}
}
return ArrayUtil.toStringArray(array);
}
public static String buildRegexp(String pattern, int exactPrefixLen, boolean allowToUpper, boolean allowToLower) {
return buildRegexp(pattern, exactPrefixLen, allowToUpper, allowToLower, false, false);
}
public static String buildRegexp(String pattern,
int exactPrefixLen,
boolean allowToUpper,
boolean allowToLower,
boolean lowerCaseWords,
boolean forCompletion) {
final int eol = pattern.indexOf('\n');
if (eol != -1) {
pattern = pattern.substring(0, eol);
}
if (pattern.length() >= MAX_LENGTH) {
pattern = pattern.substring(0, MAX_LENGTH);
}
@NonNls final StringBuilder buffer = new StringBuilder();
boolean lastIsUppercase = false;
boolean prevIsUppercase = false;
final boolean endsWithSpace = !forCompletion && StringUtil.endsWithChar(pattern, ' ');
if (!forCompletion) {
pattern = pattern.trim();
}
exactPrefixLen = Math.min(exactPrefixLen, pattern.length());
/*final boolean uppercaseOnly = containsOnlyUppercaseLetters(pattern.substring(exactPrefixLen));
if (uppercaseOnly) {
allowToLower = false;
}*/
if (exactPrefixLen > 0) {
char c = pattern.charAt(exactPrefixLen - 1);
prevIsUppercase = Character.isUpperCase(c) || Character.isDigit(c);
}
for (int i = 0; i != exactPrefixLen; ++i) {
final char c = pattern.charAt(i);
if (Character.isLetterOrDigit(c)) {
buffer.append(c);
}
else {
// for standard RegExp engine
// buffer.append("\\u");
// buffer.append(Integer.toHexString(c + 0x20000).substring(1));
// for OROMATCHER RegExp engine
buffer.append("\\").append(c);
//buffer.append(Integer.toHexString(c + 0x20000).substring(2));
}
}
if (exactPrefixLen == 0) {
buffer.append("_*"); // ignore leading underscores
}
boolean firstIdentifierLetter = (exactPrefixLen == 0);
//System.out.println("pattern = " + pattern);
for (int i = exactPrefixLen; i < pattern.length(); i++) {
final char c = pattern.charAt(i);
lastIsUppercase = false;
if (Character.isLetterOrDigit(c)) {
prevIsUppercase = false;
// This logic allows to use uppercase letters only to catch the name like PDM for PsiDocumentManager
if (Character.isUpperCase(c) || Character.isDigit(c)) {
prevIsUppercase = true;
lastIsUppercase = true;
buffer.append('(');
if (!firstIdentifierLetter) {
buffer.append("[a-z\\s0-9\\$]*");
}
buffer.append(c);
if (allowToLower) {
buffer.append('|');
buffer.append(Character.toLowerCase(c));
}
if (!firstIdentifierLetter) {
buffer.append("|[A-Za-z\\s0-9\\$]*[_-]+[");
buffer.append(c);
buffer.append(Character.toLowerCase(c));
buffer.append("]");
}
buffer.append(')');
}
else if (Character.isLowerCase(c) && allowToUpper) {
buffer.append('[');
buffer.append(c);
buffer.append(Character.toUpperCase(c));
buffer.append(']');
if (lowerCaseWords) {
buffer.append("([a-z\\s0-9\\$]*[-_]+)?");
}
}
else {
buffer.append(c);
}
firstIdentifierLetter = false;
}
else if (c == '*') {
buffer.append(".*");
firstIdentifierLetter = true;
}
else if (c == '.') {
if (!firstIdentifierLetter) {
buffer.append("[a-z\\s0-9\\$]*\\.");
}
else {
buffer.append("\\.");
}
firstIdentifierLetter = true;
}
else if (c == ' ') {
buffer.append("([a-z\\s0-9\\$_-]*[\\ _-]+)+");
firstIdentifierLetter = true;
}
else {
if (c == ':' || prevIsUppercase) {
buffer.append("[A-Za-z\\s0-9\\$]*");
}
firstIdentifierLetter = true;
// for standard RegExp engine
// buffer.append("\\u");
// buffer.append(Integer.toHexString(c + 0x20000).substring(1));
// for OROMATCHER RegExp engine
buffer.append("\\").append(c);
//buffer.append(Integer.toHexString(c + 0x20000).substring(3));
}
}
if (!endsWithSpace) {
buffer.append(".*");
}
else if (lastIsUppercase) {
buffer.append("[a-z\\s0-9\\$]*");
}
//System.out.println("rx = " + buffer.toString());
return buffer.toString();
}
/**
* Splits an identifier into words, separated with underscores or upper-case characters
* (camel-case).
*
* @param name the identifier to split.
* @return the array of strings into which the identifier has been split.
*/
public static String[] splitNameIntoWords(@NotNull String name) {
final String[] underlineDelimited = name.split("_");
List<String> result = new ArrayList<String>();
for (String word : underlineDelimited) {
addAllWords(word, result);
}
return ArrayUtil.toStringArray(result);
}
public static List<String> getSuggestionsByName(String name,
String prefix,
String suffix,
boolean upperCaseStyle,
boolean preferLongerNames, boolean isArray) {
ArrayList<String> answer = new ArrayList<String>();
String[] words = nameToWords(name);
for (int step = 0; step < words.length; step++) {
int wordCount = preferLongerNames ? words.length - step : step + 1;
String startWord = words[words.length - wordCount];
char c = startWord.charAt(0);
if( c == '_' || !Character.isJavaIdentifierStart( c ) )
{
continue;
}
answer.add(compoundSuggestion(prefix, upperCaseStyle, words, wordCount, startWord, c, isArray, false) + suffix);
answer.add(compoundSuggestion(prefix, upperCaseStyle, words, wordCount, startWord, c, isArray, true) + suffix);
}
return answer;
}
private static String compoundSuggestion(String prefix,
boolean upperCaseStyle,
String[] words,
int wordCount,
String startWord,
char c,
boolean isArray,
boolean skip_) {
StringBuilder buffer = new StringBuilder();
buffer.append(prefix);
if (upperCaseStyle) {
startWord = StringUtilRt.toUpperCase(startWord);
}
else {
if (prefix.length() == 0 || StringUtil.endsWithChar(prefix, '_')) {
startWord = startWord.toLowerCase();
}
else {
startWord = Character.toUpperCase(c) + startWord.substring(1);
}
}
buffer.append(startWord);
for (int i = words.length - wordCount + 1; i < words.length; i++) {
String word = words[i];
String prevWord = words[i - 1];
if (upperCaseStyle) {
word = StringUtilRt.toUpperCase(word);
if (prevWord.charAt(prevWord.length() - 1) != '_' && word.charAt(0) != '_') {
word = "_" + word;
}
}
else {
if (prevWord.charAt(prevWord.length() - 1) == '_') {
word = word.toLowerCase();
}
if (skip_) {
if (word.equals("_")) continue;
if (prevWord.equals("_")) {
word = StringUtil.capitalize(word);
}
}
}
buffer.append(word);
}
String suggestion = buffer.toString();
if (isArray) {
suggestion = StringUtil.pluralize(suggestion);
if (upperCaseStyle) {
suggestion = StringUtilRt.toUpperCase(suggestion);
}
}
return suggestion;
}
static boolean isWordStart(String text, int i) {
if (isWordStart(text.charAt(i))) {
return true;
}
if (!Character.isLetterOrDigit(text.charAt(i))) {
return false;
}
if (i > 0 && (isWordSeparator(text.charAt(i - 1)) || text.charAt(i - 1) == '.')) {
return true;
}
return false;
}
static boolean isWordStart(char p) {
return Character.isUpperCase(p) || Character.isDigit(p);
}
static int nextWord(String text, int start) {
if (!Character.isLetterOrDigit(text.charAt(start))) {
return start + 1;
}
int i = start;
while (i < text.length() && isWordStart(text, i)) {
i++;
}
if (i > start + 1) {
if (i == text.length() || !Character.isLetterOrDigit(text.charAt(i))) {
return i;
}
return i - 1;
}
while (i < text.length() && !isWordStart(text, i) && Character.isLetterOrDigit(text.charAt(i))) {
i++;
}
return i;
}
private static void addAllWords(String text, List<String> result) {
int start = 0;
while (start < text.length()) {
int next = nextWord(text, start);
result.add(text.substring(start, next));
start = next;
}
}
static boolean isWordSeparator(char c) {
return Character.isWhitespace(c) || c == '_' || c == '-' || c == ':';
}
/**
* @deprecated use com.intellij.util.text.Matcher
*/
public interface Matcher {
boolean matches(String name);
}
@SuppressWarnings("UnusedDeclaration")
@Deprecated
public static com.intellij.util.text.Matcher buildCompletionMatcher(String pattern, int exactPrefixLen, boolean allowToUpper, boolean allowToLower) {
MatchingCaseSensitivity options = !allowToLower && !allowToUpper ? MatchingCaseSensitivity.ALL : exactPrefixLen > 0 ? MatchingCaseSensitivity.FIRST_LETTER : MatchingCaseSensitivity.NONE;
return buildMatcher(pattern, options);
}
public static com.intellij.util.text.Matcher buildMatcher(String pattern, int exactPrefixLen, boolean allowToUpper, boolean allowToLower) {
MatchingCaseSensitivity options = !allowToLower && !allowToUpper ? MatchingCaseSensitivity.ALL : exactPrefixLen > 0 ? MatchingCaseSensitivity.FIRST_LETTER : MatchingCaseSensitivity.NONE;
return buildMatcher(pattern, options);
}
@SuppressWarnings("UnusedParameters")
@Deprecated
public static com.intellij.util.text.Matcher buildMatcher(String pattern, int exactPrefixLen, boolean allowToUpper, boolean allowToLower, boolean lowerCaseWords) {
MatchingCaseSensitivity options = !allowToLower && !allowToUpper ? MatchingCaseSensitivity.ALL : exactPrefixLen > 0 ? MatchingCaseSensitivity.FIRST_LETTER : MatchingCaseSensitivity.NONE;
return buildMatcher(pattern, options);
}
public static MinusculeMatcher buildMatcher(final String pattern, MatchingCaseSensitivity options) {
return new MinusculeMatcher(pattern, options);
}
public enum MatchingCaseSensitivity {
NONE, FIRST_LETTER, ALL
}
}
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.codeStyle;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.util.text.StringUtilRt;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public class NameUtil {
private static final Function<String,String> LOWERCASE_MAPPING = new Function<String, String>() {
public String fun(final String s) {
return s.toLowerCase();
}
};
private static final int MAX_LENGTH = 40;
private NameUtil() {}
public static List<String> nameToWordsLowerCase(String name){
return ContainerUtil.map(nameToWords(name), LOWERCASE_MAPPING);
}
public static String[] nameToWords(String name){
ArrayList<String> array = new ArrayList<String>();
int index = 0;
int wordStart;
while(index < name.length()){
wordStart = index;
int upperCaseCount = 0;
int lowerCaseCount = 0;
int digitCount = 0;
int specialCount = 0;
while(index < name.length()){
char c = name.charAt(index);
if (Character.isDigit(c)){
if (upperCaseCount > 0 || lowerCaseCount > 0 || specialCount > 0) break;
digitCount++;
}
else if (Character.isUpperCase(c)){
if (lowerCaseCount > 0 || digitCount > 0 || specialCount > 0) break;
upperCaseCount++;
}
else if (Character.isLowerCase(c)){
if (digitCount > 0 || specialCount > 0) break;
if (upperCaseCount > 1) {
index--;
break;
}
lowerCaseCount++;
}
else{
if (upperCaseCount > 0 || lowerCaseCount > 0 || digitCount > 0) break;
specialCount++;
}
index++;
}
String word = name.substring(wordStart, index);
if (!StringUtil.isEmptyOrSpaces(word)) {
array.add(word);
}
}
return ArrayUtil.toStringArray(array);
}
public static String buildRegexp(String pattern, int exactPrefixLen, boolean allowToUpper, boolean allowToLower) {
return buildRegexp(pattern, exactPrefixLen, allowToUpper, allowToLower, false, false);
}
public static String buildRegexp(String pattern,
int exactPrefixLen,
boolean allowToUpper,
boolean allowToLower,
boolean lowerCaseWords,
boolean forCompletion) {
final int eol = pattern.indexOf('\n');
if (eol != -1) {
pattern = pattern.substring(0, eol);
}
if (pattern.length() >= MAX_LENGTH) {
pattern = pattern.substring(0, MAX_LENGTH);
}
@NonNls final StringBuilder buffer = new StringBuilder();
boolean lastIsUppercase = false;
boolean prevIsUppercase = false;
final boolean endsWithSpace = !forCompletion && StringUtil.endsWithChar(pattern, ' ');
if (!forCompletion) {
pattern = pattern.trim();
}
exactPrefixLen = Math.min(exactPrefixLen, pattern.length());
/*final boolean uppercaseOnly = containsOnlyUppercaseLetters(pattern.substring(exactPrefixLen));
if (uppercaseOnly) {
allowToLower = false;
}*/
if (exactPrefixLen > 0) {
char c = pattern.charAt(exactPrefixLen - 1);
prevIsUppercase = Character.isUpperCase(c) || Character.isDigit(c);
}
for (int i = 0; i != exactPrefixLen; ++i) {
final char c = pattern.charAt(i);
if (Character.isLetterOrDigit(c)) {
buffer.append(c);
}
else {
// for standard RegExp engine
// buffer.append("\\u");
// buffer.append(Integer.toHexString(c + 0x20000).substring(1));
// for OROMATCHER RegExp engine
buffer.append("\\").append(c);
//buffer.append(Integer.toHexString(c + 0x20000).substring(2));
}
}
if (exactPrefixLen == 0) {
buffer.append("_*"); // ignore leading underscores
}
boolean firstIdentifierLetter = (exactPrefixLen == 0);
//System.out.println("pattern = " + pattern);
for (int i = exactPrefixLen; i < pattern.length(); i++) {
final char c = pattern.charAt(i);
lastIsUppercase = false;
if (Character.isLetterOrDigit(c)) {
prevIsUppercase = false;
// This logic allows to use uppercase letters only to catch the name like PDM for PsiDocumentManager
if (Character.isUpperCase(c) || Character.isDigit(c)) {
prevIsUppercase = true;
lastIsUppercase = true;
buffer.append('(');
if (!firstIdentifierLetter) {
buffer.append("[a-z\\s0-9\\$]*");
}
buffer.append(c);
if (allowToLower) {
buffer.append('|');
buffer.append(Character.toLowerCase(c));
}
if (!firstIdentifierLetter) {
buffer.append("|[A-Za-z\\s0-9\\$]*[_-]+[");
buffer.append(c);
buffer.append(Character.toLowerCase(c));
buffer.append("]");
}
buffer.append(')');
}
else if (Character.isLowerCase(c) && allowToUpper) {
buffer.append('[');
buffer.append(c);
buffer.append(Character.toUpperCase(c));
buffer.append(']');
if (lowerCaseWords) {
buffer.append("([a-z\\s0-9\\$]*[-_]+)?");
}
}
else {
buffer.append(c);
}
firstIdentifierLetter = false;
}
else if (c == '*') {
buffer.append(".*");
firstIdentifierLetter = true;
}
else if (c == '.') {
if (!firstIdentifierLetter) {
buffer.append("[a-z\\s0-9\\$]*\\.");
}
else {
buffer.append("\\.");
}
firstIdentifierLetter = true;
}
else if (c == ' ') {
buffer.append("([a-z\\s0-9\\$_-]*[\\ _-]+)+");
firstIdentifierLetter = true;
}
else {
if (c == ':' || prevIsUppercase) {
buffer.append("[A-Za-z\\s0-9\\$]*");
}
firstIdentifierLetter = true;
// for standard RegExp engine
// buffer.append("\\u");
// buffer.append(Integer.toHexString(c + 0x20000).substring(1));
// for OROMATCHER RegExp engine
buffer.append("\\").append(c);
//buffer.append(Integer.toHexString(c + 0x20000).substring(3));
}
}
if (!endsWithSpace) {
buffer.append(".*");
}
else if (lastIsUppercase) {
buffer.append("[a-z\\s0-9\\$]*");
}
//System.out.println("rx = " + buffer.toString());
return buffer.toString();
}
/**
* Splits an identifier into words, separated with underscores or upper-case characters
* (camel-case).
*
* @param name the identifier to split.
* @return the array of strings into which the identifier has been split.
*/
public static String[] splitNameIntoWords(@NotNull String name) {
final String[] underlineDelimited = name.split("_");
List<String> result = new ArrayList<String>();
for (String word : underlineDelimited) {
addAllWords(word, result);
}
return ArrayUtil.toStringArray(result);
}
public static List<String> getSuggestionsByName(String name,
String prefix,
String suffix,
boolean upperCaseStyle,
boolean preferLongerNames, boolean isArray) {
ArrayList<String> answer = new ArrayList<String>();
String[] words = nameToWords(name);
for (int step = 0; step < words.length; step++) {
int wordCount = preferLongerNames ? words.length - step : step + 1;
String startWord = words[words.length - wordCount];
char c = startWord.charAt(0);
if( c == '_' || !Character.isJavaIdentifierStart( c ) )
{
continue;
}
answer.add(compoundSuggestion(prefix, upperCaseStyle, words, wordCount, startWord, c, isArray, false) + suffix);
answer.add(compoundSuggestion(prefix, upperCaseStyle, words, wordCount, startWord, c, isArray, true) + suffix);
}
return answer;
}
private static String compoundSuggestion(String prefix,
boolean upperCaseStyle,
String[] words,
int wordCount,
String startWord,
char c,
boolean isArray,
boolean skip_) {
StringBuilder buffer = new StringBuilder();
buffer.append(prefix);
if (upperCaseStyle) {
startWord = StringUtilRt.toUpperCase(startWord);
}
else {
if (prefix.length() == 0 || StringUtil.endsWithChar(prefix, '_')) {
startWord = startWord.toLowerCase();
}
else {
startWord = Character.toUpperCase(c) + startWord.substring(1);
}
}
buffer.append(startWord);
for (int i = words.length - wordCount + 1; i < words.length; i++) {
String word = words[i];
String prevWord = words[i - 1];
if (upperCaseStyle) {
word = StringUtilRt.toUpperCase(word);
if (prevWord.charAt(prevWord.length() - 1) != '_' && word.charAt(0) != '_') {
word = "_" + word;
}
}
else {
if (prevWord.charAt(prevWord.length() - 1) == '_') {
word = word.toLowerCase();
}
if (skip_) {
if (word.equals("_")) continue;
if (prevWord.equals("_")) {
word = StringUtil.capitalize(word);
}
}
}
buffer.append(word);
}
String suggestion = buffer.toString();
if (isArray) {
suggestion = StringUtil.pluralize(suggestion);
if (upperCaseStyle) {
suggestion = StringUtilRt.toUpperCase(suggestion);
}
}
return suggestion;
}
static boolean isWordStart(String text, int i) {
if (isWordStart(text.charAt(i))) {
return true;
}
if (!Character.isLetterOrDigit(text.charAt(i))) {
return false;
}
if (i == 0 || !Character.isLetterOrDigit(text.charAt(i - 1))) {
return true;
}
return false;
}
static boolean isWordStart(char p) {
return Character.isUpperCase(p) || Character.isDigit(p);
}
static int nextWord(String text, int start) {
if (!Character.isLetterOrDigit(text.charAt(start))) {
return start + 1;
}
int i = start;
while (i < text.length() && isWordStart(text, i)) {
i++;
}
if (i > start + 1) {
if (i == text.length() || !Character.isLetterOrDigit(text.charAt(i))) {
return i;
}
return i - 1;
}
while (i < text.length() && !isWordStart(text, i) && Character.isLetterOrDigit(text.charAt(i))) {
i++;
}
return i;
}
private static void addAllWords(String text, List<String> result) {
int start = 0;
while (start < text.length()) {
int next = nextWord(text, start);
result.add(text.substring(start, next));
start = next;
}
}
static boolean isWordSeparator(char c) {
return Character.isWhitespace(c) || c == '_' || c == '-' || c == ':';
}
/**
* @deprecated use com.intellij.util.text.Matcher
*/
public interface Matcher {
boolean matches(String name);
}
@SuppressWarnings("UnusedDeclaration")
@Deprecated
public static com.intellij.util.text.Matcher buildCompletionMatcher(String pattern, int exactPrefixLen, boolean allowToUpper, boolean allowToLower) {
MatchingCaseSensitivity options = !allowToLower && !allowToUpper ? MatchingCaseSensitivity.ALL : exactPrefixLen > 0 ? MatchingCaseSensitivity.FIRST_LETTER : MatchingCaseSensitivity.NONE;
return buildMatcher(pattern, options);
}
public static com.intellij.util.text.Matcher buildMatcher(String pattern, int exactPrefixLen, boolean allowToUpper, boolean allowToLower) {
MatchingCaseSensitivity options = !allowToLower && !allowToUpper ? MatchingCaseSensitivity.ALL : exactPrefixLen > 0 ? MatchingCaseSensitivity.FIRST_LETTER : MatchingCaseSensitivity.NONE;
return buildMatcher(pattern, options);
}
@SuppressWarnings("UnusedParameters")
@Deprecated
public static com.intellij.util.text.Matcher buildMatcher(String pattern, int exactPrefixLen, boolean allowToUpper, boolean allowToLower, boolean lowerCaseWords) {
MatchingCaseSensitivity options = !allowToLower && !allowToUpper ? MatchingCaseSensitivity.ALL : exactPrefixLen > 0 ? MatchingCaseSensitivity.FIRST_LETTER : MatchingCaseSensitivity.NONE;
return buildMatcher(pattern, options);
}
public static MinusculeMatcher buildMatcher(final String pattern, MatchingCaseSensitivity options) {
return new MinusculeMatcher(pattern, options);
}
public enum MatchingCaseSensitivity {
NONE, FIRST_LETTER, ALL
}
}
@@ -334,7 +334,7 @@ public class AndroidLayoutDomTest extends AndroidDomTest {
}
public void testIdCompletion2() throws Throwable {
doTestCompletionVariants("idcompl2.xml", "@android:id/text1", "@android:id/text2");
doTestCompletionVariants("idcompl2.xml", "@android:id/text1", "@android:id/text2", "@android:id/inputExtractEditText", "@android:id/startSelectingText", "@android:id/stopSelectingText");
}
public void testIdHighlighting() throws Throwable {
@@ -17,6 +17,7 @@
package org.jetbrains.android.dom;
import com.android.sdklib.SdkConstants;
import com.intellij.codeInsight.completion.CompletionType;
import com.intellij.codeInsight.completion.impl.CamelHumpMatcher;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.intention.IntentionAction;
@@ -72,7 +73,11 @@ public class AndroidValueResourcesTest extends AndroidDomTest {
}
public void testStyles2() throws Throwable {
toTestCompletion("styles2.xml", "styles2_after.xml");
VirtualFile file = copyFileToProject("styles2.xml");
myFixture.configureFromExistingVirtualFile(file);
myFixture.complete(CompletionType.BASIC);
myFixture.type('\n');
myFixture.checkResultByFile(testFolder + '/' + "styles2_after.xml");
}
public void testStyles3() throws Throwable {
@@ -155,7 +160,11 @@ public class AndroidValueResourcesTest extends AndroidDomTest {
}
public void testStylesAttrNameWithoutPrefix() throws Throwable {
doTestCompletion();
VirtualFile file = copyFileToProject(getTestName(true) + ".xml");
myFixture.configureFromExistingVirtualFile(file);
myFixture.complete(CompletionType.BASIC);
myFixture.type('\n');
myFixture.checkResultByFile(testFolder + '/' + getTestName(true) + "_after.xml");
}
public void testMoreTypes() throws Throwable {
@@ -76,7 +76,12 @@ class GrCompletionWithLibraryTest extends GroovyCompletionTestBase {
public void testInjectMethodForMap() throws Throwable {doBasicTest();}
public void testClosureDefaultParameterInEachMethod() throws Throwable {doBasicTest();}
public void testEachMethodForRanges() throws Throwable {doBasicTest();}
public void testEachMethodForEnumRanges() throws Throwable {doBasicTest();}
public void testEachMethodForEnumRanges() throws Throwable {
myFixture.configureByFile(getTestName(false) + ".groovy")
myFixture.completeBasic()
myFixture.type('\n')
myFixture.checkResultByFile(getTestName(false) + "_after.groovy");
}
public void testPrintlnSpace() { checkCompletion 'print<caret>', 'l ', "println <caret>" }
public void testHashCodeSpace() { checkCompletion 'if ("".h<caret>', ' ', 'if ("".hashCode() <caret>' }
@@ -4,5 +4,6 @@ public String fooooo(){}
MyClass.fo<caret>
-----
findIndexOf
forName
forName
@@ -284,6 +284,7 @@ public class XmlCompletionTest extends LightCodeInsightFixtureTestCase {
public void testEntityRefCompletion2() throws Exception {
configureByFiles("28.xml", "28.ent");
complete();
type('\n');
checkResultByFile("28_after.xml");
}