mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -22,8 +22,9 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class NameEnvironment extends UserDataHolderBase {
|
||||
public static final int OBJECT_NAME = IndexTree.hashIdentifier("Object");
|
||||
public final QualifiedName empty;
|
||||
public final QualifiedName java_lang_Object;
|
||||
public final QualifiedName java_lang;
|
||||
public final QualifiedName java_lang_Enum;
|
||||
public final QualifiedName[] annotation;
|
||||
public final NamesEnumerator myNamesEnumerator;
|
||||
@@ -31,7 +32,7 @@ public class NameEnvironment extends UserDataHolderBase {
|
||||
public NameEnvironment() {
|
||||
myNamesEnumerator = new NamesEnumerator();
|
||||
empty = myNamesEnumerator.getFullName(new int[]{}, true);
|
||||
java_lang_Object = fromString("java.lang.Object", true);
|
||||
java_lang = fromString("java.lang", true);
|
||||
java_lang_Enum = fromString("java.lang.Enum", true);
|
||||
annotation = new QualifiedName[]{fromString("java.lang.annotation.Annotation", true)};
|
||||
}
|
||||
@@ -57,13 +58,6 @@ public class NameEnvironment extends UserDataHolderBase {
|
||||
return ids[ids.length - 1];
|
||||
}
|
||||
|
||||
public QualifiedName qualifiedName(Symbol owner, int shortName) {
|
||||
if (shortName == NamesEnumerator.NO_NAME || owner == null || owner.myQualifiedName == null) {
|
||||
return null;
|
||||
}
|
||||
return qualifiedName(owner.myQualifiedName, shortName, true);
|
||||
}
|
||||
|
||||
public QualifiedName qualifiedName(QualifiedName prefix, int shortName, boolean create) {
|
||||
if (shortName == NamesEnumerator.NO_NAME)
|
||||
return null;
|
||||
|
||||
@@ -176,7 +176,7 @@ public class SingleClassHierarchy extends ClassHierarchy {
|
||||
// compacting
|
||||
result = Arrays.copyOf(result, i);
|
||||
|
||||
Arrays.sort(result, Comparator.comparing(a -> a.myFileId));
|
||||
Arrays.sort(result, (a1, a2) -> Integer.compare(a1.myFileId, a2.myFileId));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ package com.intellij.psi.stubsHierarchy.impl;
|
||||
|
||||
import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree;
|
||||
import com.intellij.psi.stubsHierarchy.stubs.*;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -38,47 +39,51 @@ public class StubEnter {
|
||||
|
||||
void unitEnter(Unit tree) {
|
||||
PackageSymbol pkg = tree.myPackageId != null ? mySymbols.enterPackage(tree.myPackageId) : mySymbols.myRootPackage;
|
||||
enter(tree.myClasses, tree.myUnitInfo, pkg);
|
||||
enter(tree.myClasses, tree.myUnitInfo, pkg, pkg.myQualifiedName);
|
||||
}
|
||||
|
||||
private void enter(ClassDeclaration[] trees, UnitInfo info, Symbol owner) {
|
||||
private void enter(ClassDeclaration[] trees, UnitInfo info, Symbol owner, @Nullable QualifiedName ownerName) {
|
||||
for (ClassDeclaration tree : trees) {
|
||||
enter(tree, info, owner);
|
||||
enter(tree, info, owner, ownerName);
|
||||
}
|
||||
}
|
||||
|
||||
private ClassSymbol[] enter(Declaration[] trees, UnitInfo info, Symbol owner) {
|
||||
private ClassSymbol[] enter(Declaration[] trees, UnitInfo info, Symbol owner, @Nullable QualifiedName ownerName) {
|
||||
ClassSymbol[] members = new ClassSymbol[trees.length];
|
||||
int i = 0;
|
||||
for (Declaration tree : trees) {
|
||||
ClassSymbol member = enter(tree, info, owner);
|
||||
ClassSymbol member = enter(tree, info, owner, ownerName);
|
||||
if (member != null && member.myShortName != 0) {
|
||||
members[i++] = member;
|
||||
}
|
||||
}
|
||||
members = members.length == 0 ? ClassSymbol.EMPTY_ARRAY : Arrays.copyOf(members, i);
|
||||
if (i == 0) return ClassSymbol.EMPTY_ARRAY;
|
||||
|
||||
if (i < members.length) {
|
||||
members = Arrays.copyOf(members, i);
|
||||
}
|
||||
Arrays.sort(members, CLASS_SYMBOL_BY_NAME_COMPARATOR);
|
||||
return members;
|
||||
}
|
||||
|
||||
private ClassSymbol enter(Declaration tree, UnitInfo info, Symbol owner) {
|
||||
private ClassSymbol enter(Declaration tree, UnitInfo info, Symbol owner, QualifiedName ownerName) {
|
||||
if (tree instanceof ClassDeclaration) {
|
||||
return classEnter((ClassDeclaration)tree, info, owner);
|
||||
return classEnter((ClassDeclaration)tree, info, owner, ownerName);
|
||||
}
|
||||
if (tree instanceof MemberDeclaration) {
|
||||
memberEnter((MemberDeclaration)tree, info, owner);
|
||||
memberEnter((MemberDeclaration)tree, info, owner, ownerName);
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void memberEnter(MemberDeclaration tree, UnitInfo info, Symbol owner) {
|
||||
private void memberEnter(MemberDeclaration tree, UnitInfo info, Symbol owner, @Nullable QualifiedName ownerName) {
|
||||
MemberSymbol mc = new MemberSymbol(owner);
|
||||
ClassSymbol[] members = enter(tree.myDeclarations, info, mc);
|
||||
ClassSymbol[] members = enter(tree.myDeclarations, info, mc, ownerName);
|
||||
mc.setMembers(members);
|
||||
}
|
||||
|
||||
private ClassSymbol classEnter(ClassDeclaration tree, UnitInfo info, Symbol owner) {
|
||||
private ClassSymbol classEnter(ClassDeclaration tree, UnitInfo info, Symbol owner, @Nullable QualifiedName ownerName) {
|
||||
int flags = checkFlags(tree.mods, owner);
|
||||
if (info.getType() == IndexTree.BYTECODE) {
|
||||
flags |= IndexTree.COMPILED;
|
||||
@@ -88,13 +93,17 @@ public class StubEnter {
|
||||
supers = myNameEnvironment.annotation;
|
||||
}
|
||||
|
||||
ClassSymbol classSymbol = mySymbols.enterClass(tree.myClassAnchor, flags, tree.myName, owner, info, supers);
|
||||
int name = tree.myName;
|
||||
QualifiedName qname = name == NamesEnumerator.NO_NAME || ownerName == null ? null
|
||||
: myNameEnvironment.qualifiedName(ownerName, name, true);
|
||||
ClassSymbol classSymbol = mySymbols.enterClass(tree.myClassAnchor, flags, name, owner, info, supers, qname);
|
||||
|
||||
if (uncompleted != null) {
|
||||
if (uncompleted != null) {
|
||||
uncompleted.add(classSymbol);
|
||||
}
|
||||
ClassSymbol[] members = enter(tree.myDeclarations, info, classSymbol);
|
||||
classSymbol.setMembers(members);
|
||||
if (tree.myDeclarations.length > 0) {
|
||||
classSymbol.setMembers(enter(tree.myDeclarations, info, classSymbol, qname));
|
||||
}
|
||||
return classSymbol;
|
||||
}
|
||||
|
||||
|
||||
+44
-32
@@ -29,6 +29,23 @@ public class StubHierarchyConnector {
|
||||
myResolve = new StubResolver(symbols, this);
|
||||
}
|
||||
|
||||
private void resolveName(Symbol.ClassSymbol place, QualifiedName name, Set<Symbol.ClassSymbol> result) throws IncompleteHierarchyException {
|
||||
if (place.isCompiled()) {
|
||||
Symbol.ClassSymbol[] candidates = myResolve.findGlobalType(name);
|
||||
if (candidates.length == 0) {
|
||||
throw new IncompleteHierarchyException();
|
||||
}
|
||||
|
||||
Collections.addAll(result, candidates);
|
||||
} else {
|
||||
for (Symbol symbol : myResolve.resolveBase(place, name.myComponents)) {
|
||||
if (symbol instanceof Symbol.ClassSymbol) {
|
||||
result.add((Symbol.ClassSymbol)symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void connect(Symbol sym) {
|
||||
Symbol.ClassSymbol c = (Symbol.ClassSymbol) sym;
|
||||
|
||||
@@ -36,45 +53,40 @@ public class StubHierarchyConnector {
|
||||
((Symbol.ClassSymbol)c.myOwner).connect(this);
|
||||
}
|
||||
|
||||
Object supers = c.mySuperClasses;
|
||||
if (supers == null) {
|
||||
c.setSupers(Collections.emptySet());
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine supertype.
|
||||
Set<Symbol> supertypes = new HashSet<Symbol>();
|
||||
for (QualifiedName name : c.mySuperNames) {
|
||||
if (c.isCompiled()) {
|
||||
if (name != null) {
|
||||
Collections.addAll(supertypes, myResolve.findGlobalType(name));
|
||||
Set<Symbol.ClassSymbol> supertypes = new HashSet<>();
|
||||
|
||||
try {
|
||||
if (supers instanceof QualifiedName[]) {
|
||||
for (QualifiedName name : (QualifiedName[])supers) {
|
||||
resolveName(c, name, supertypes);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
supertypes.addAll(myResolve.resolveBase(c, name.myComponents));
|
||||
}
|
||||
catch (IncompleteHierarchyException ignore) {
|
||||
c.markHierarchyIncomplete();
|
||||
break;
|
||||
}
|
||||
resolveName(c, (QualifiedName)supers, supertypes);
|
||||
}
|
||||
}
|
||||
|
||||
if (c.myQualifiedName == myNameEnvironment.java_lang_Object || c.isHierarchyIncomplete()) {
|
||||
c.mySuperClasses = Symbol.ClassSymbol.EMPTY_ARRAY;
|
||||
} else {
|
||||
for (Iterator<Symbol> iter = supertypes.iterator(); iter.hasNext();) {
|
||||
Symbol s = iter.next();
|
||||
if (!(s instanceof Symbol.ClassSymbol) || s.myQualifiedName == myNameEnvironment.java_lang_Object) {
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
if (supertypes.isEmpty()) {
|
||||
c.mySuperClasses = Symbol.ClassSymbol.EMPTY_ARRAY;
|
||||
}
|
||||
else {
|
||||
//noinspection SuspiciousToArrayCall
|
||||
c.mySuperClasses = supertypes.toArray(new Symbol.ClassSymbol[supertypes.size()]);
|
||||
}
|
||||
catch (IncompleteHierarchyException ignore) {
|
||||
c.markHierarchyIncomplete();
|
||||
return;
|
||||
}
|
||||
|
||||
// cleaning up
|
||||
c.mySuperNames = null;
|
||||
c.myUnitInfo = null;
|
||||
for (Iterator<Symbol.ClassSymbol> iterator = supertypes.iterator(); iterator.hasNext();) {
|
||||
if (isJavaLangObject(iterator.next())) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
c.setSupers(supertypes);
|
||||
}
|
||||
|
||||
private boolean isJavaLangObject(Symbol s) {
|
||||
return s.myShortName == NameEnvironment.OBJECT_NAME &&
|
||||
s.myOwner instanceof Symbol.PackageSymbol &&
|
||||
((Symbol.PackageSymbol)s.myOwner).myQualifiedName == myNameEnvironment.java_lang;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,35 +103,11 @@ public class StubResolver {
|
||||
if (!processed.add(s)) {
|
||||
return;
|
||||
}
|
||||
findImmediateMemberType(s, name, symbols);
|
||||
processMembers(s.getMembers(), name, symbols, false);
|
||||
if (s.isClass())
|
||||
findInheritedMemberType((Symbol.ClassSymbol)s, name, symbols, processed);
|
||||
}
|
||||
|
||||
private static void findImmediateMemberType(Symbol s, int name, Set<Symbol> symbols) {
|
||||
Symbol.ClassSymbol[] members = s.members();
|
||||
int index = getIndex(name, members);
|
||||
if (index < 0) return;
|
||||
|
||||
// elem
|
||||
Symbol.ClassSymbol member = members[index];
|
||||
symbols.add(member);
|
||||
// on the left
|
||||
int i = index - 1;
|
||||
while (i >= 0 && members[i].myShortName == name) {
|
||||
member = members[i];
|
||||
symbols.add(member);
|
||||
i--;
|
||||
}
|
||||
// on the right
|
||||
i = index + 1;
|
||||
while (i < members.length && members[i].myShortName == name) {
|
||||
member = members[i];
|
||||
symbols.add(member);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
private void findInheritedMemberType(Symbol.ClassSymbol c, int name, Set<Symbol> symbols, Set<Symbol> processed)
|
||||
throws IncompleteHierarchyException {
|
||||
for (Symbol.ClassSymbol st : c.getSuperClasses(myConnector))
|
||||
@@ -204,25 +180,25 @@ public class StubResolver {
|
||||
return;
|
||||
for (Symbol.ClassSymbol c : cs.getSuperClasses(myConnector))
|
||||
importFrom(c);
|
||||
importMember(cs.members(), name, symbols, true);
|
||||
processMembers(cs.getMembers(), name, symbols, true);
|
||||
}
|
||||
}.importFrom(tsym);
|
||||
}
|
||||
|
||||
private static void importMember(Symbol.ClassSymbol[] members, int name, Set<Symbol> symbols, boolean isStatic) {
|
||||
private static void processMembers(Symbol.ClassSymbol[] members, int name, Set<Symbol> symbols, boolean requireStatic) {
|
||||
int index = getIndex(name, members);
|
||||
if (index < 0) return;
|
||||
|
||||
// elem
|
||||
Symbol.ClassSymbol member = members[index];
|
||||
if (!isStatic || member.isStatic()) {
|
||||
if (!requireStatic || member.isStatic()) {
|
||||
symbols.add(member);
|
||||
}
|
||||
// on the left
|
||||
int i = index - 1;
|
||||
while (i >= 0 && members[i].myShortName == name) {
|
||||
member = members[i];
|
||||
if (!isStatic || member.isStatic()) {
|
||||
if (!requireStatic || member.isStatic()) {
|
||||
symbols.add(member);
|
||||
}
|
||||
i--;
|
||||
@@ -231,7 +207,7 @@ public class StubResolver {
|
||||
i = index + 1;
|
||||
while (i < members.length && members[i].myShortName == name) {
|
||||
member = members[i];
|
||||
if (!isStatic || member.isStatic()) {
|
||||
if (!requireStatic || member.isStatic()) {
|
||||
symbols.add(member);
|
||||
}
|
||||
i++;
|
||||
|
||||
@@ -21,7 +21,9 @@ import com.intellij.util.BitUtil;
|
||||
import gnu.trove.TIntHashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Java symbols needed for hierarchy building. Mostly classes ({@link ClassSymbol}) or packages ({@link PackageSymbol}),
|
||||
@@ -30,13 +32,11 @@ import java.util.Comparator;
|
||||
public abstract class Symbol {
|
||||
public int myFlags;
|
||||
public int myShortName;
|
||||
public final QualifiedName myQualifiedName;
|
||||
public final Symbol myOwner;
|
||||
|
||||
public Symbol(int flags, Symbol owner, QualifiedName qualifiedName, int name) {
|
||||
public Symbol(int flags, Symbol owner, int name) {
|
||||
this.myFlags = flags;
|
||||
this.myOwner = owner;
|
||||
this.myQualifiedName = qualifiedName;
|
||||
this.myShortName = name;
|
||||
}
|
||||
|
||||
@@ -45,13 +45,10 @@ public abstract class Symbol {
|
||||
return myShortName;
|
||||
}
|
||||
|
||||
public ClassSymbol[] members() {
|
||||
ClassSymbol[] getMembers() {
|
||||
return ClassSymbol.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
public void setMembers(ClassSymbol[] members) {
|
||||
}
|
||||
|
||||
public boolean isStatic() {
|
||||
return BitUtil.isSet(myFlags, IndexTree.STATIC);
|
||||
}
|
||||
@@ -77,35 +74,40 @@ public abstract class Symbol {
|
||||
}
|
||||
|
||||
public static class PackageSymbol extends Symbol {
|
||||
final QualifiedName myQualifiedName;
|
||||
|
||||
public PackageSymbol(Symbol owner, QualifiedName fullname, int name) {
|
||||
super(IndexTree.PACKAGE, owner, fullname, name);
|
||||
setMembers(ClassSymbol.EMPTY_ARRAY);
|
||||
super(IndexTree.PACKAGE, owner, name);
|
||||
myQualifiedName = fullname;
|
||||
}
|
||||
}
|
||||
|
||||
/** A class for class symbols
|
||||
*/
|
||||
public static class ClassSymbol extends Symbol {
|
||||
public static class ClassSymbol extends MemberSymbol {
|
||||
private static final int HIERARCHY_INCOMPLETE = 1 << 20;
|
||||
private static final int CONNECT_STARTED = 1 << 21;
|
||||
public static final ClassSymbol[] EMPTY_ARRAY = new ClassSymbol[0];
|
||||
|
||||
final StubClassAnchor myClassAnchor;
|
||||
ClassSymbol[] mySuperClasses;
|
||||
|
||||
/**
|
||||
* null for empty 'supers' list
|
||||
* ClassSymbol/QualifiedName for a single resolved/unresolved super
|
||||
* ClassSymbol[]/QualifiedName[] for multiple resolved/unresolved supers
|
||||
*/
|
||||
Object mySuperClasses;
|
||||
UnitInfo myUnitInfo;
|
||||
QualifiedName[] mySuperNames;
|
||||
private ClassSymbol[] myMembers;
|
||||
|
||||
ClassSymbol(StubClassAnchor classAnchor,
|
||||
int flags,
|
||||
Symbol owner,
|
||||
QualifiedName fullname,
|
||||
int name,
|
||||
UnitInfo unitInfo,
|
||||
QualifiedName[] supers) {
|
||||
super(flags | IndexTree.CLASS, owner, fullname, name);
|
||||
int flags,
|
||||
Symbol owner,
|
||||
int name,
|
||||
UnitInfo unitInfo,
|
||||
QualifiedName[] supers) {
|
||||
super(flags | IndexTree.CLASS, owner, name);
|
||||
this.myClassAnchor = classAnchor;
|
||||
this.mySuperNames = supers;
|
||||
this.mySuperClasses = supers.length == 0 ? null : supers.length == 1 ? supers[0] : supers;
|
||||
this.myUnitInfo = unitInfo;
|
||||
}
|
||||
|
||||
@@ -137,28 +139,27 @@ public abstract class Symbol {
|
||||
@NotNull
|
||||
ClassSymbol[] rawSuperClasses() {
|
||||
assert isConnectStarted();
|
||||
return mySuperClasses == null ? EMPTY_ARRAY : mySuperClasses;
|
||||
return mySuperClasses instanceof ClassSymbol ? new ClassSymbol[]{(ClassSymbol)mySuperClasses} :
|
||||
mySuperClasses instanceof ClassSymbol[] ? (ClassSymbol[])mySuperClasses :
|
||||
EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
boolean isCompiled() {
|
||||
return BitUtil.isSet(myFlags, IndexTree.COMPILED);
|
||||
}
|
||||
|
||||
public ClassSymbol[] members() {
|
||||
return myMembers;
|
||||
}
|
||||
|
||||
public void setMembers(ClassSymbol[] members) {
|
||||
this.myMembers = members;
|
||||
}
|
||||
|
||||
void markHierarchyIncomplete() {
|
||||
mySuperClasses = EMPTY_ARRAY;
|
||||
mySuperNames = null;
|
||||
myUnitInfo = null;
|
||||
setSupers(Collections.emptySet());
|
||||
myFlags = BitUtil.set(myFlags, HIERARCHY_INCOMPLETE, true);
|
||||
}
|
||||
|
||||
void setSupers(Set<ClassSymbol> supers) {
|
||||
mySuperClasses = supers.isEmpty() ? null :
|
||||
supers.size() == 1 ? supers.iterator().next() :
|
||||
supers.toArray(new ClassSymbol[supers.size()]);
|
||||
myUnitInfo = null;
|
||||
}
|
||||
|
||||
boolean isHierarchyIncomplete() {
|
||||
return BitUtil.isSet(myFlags, HIERARCHY_INCOMPLETE);
|
||||
}
|
||||
@@ -182,15 +183,27 @@ public abstract class Symbol {
|
||||
* Represents methods, fields and other constructs that may contain anonymous or local classes.
|
||||
*/
|
||||
public static class MemberSymbol extends Symbol {
|
||||
private ClassSymbol[] myMembers;
|
||||
public MemberSymbol(Symbol owner) {
|
||||
super(IndexTree.MEMBER, owner, null, NamesEnumerator.NO_NAME);
|
||||
/**
|
||||
* null when no members, or a single ClassSymbol, or ClassSymbol[]
|
||||
*/
|
||||
private Object myMembers = null;
|
||||
|
||||
MemberSymbol(Symbol owner) {
|
||||
super(IndexTree.MEMBER, owner, NamesEnumerator.NO_NAME);
|
||||
}
|
||||
public ClassSymbol[] members() {
|
||||
return myMembers;
|
||||
|
||||
MemberSymbol(int flags, Symbol owner, int name) {
|
||||
super(flags, owner, name);
|
||||
}
|
||||
public void setMembers(ClassSymbol[] members) {
|
||||
this.myMembers = members;
|
||||
|
||||
ClassSymbol[] getMembers() {
|
||||
return myMembers == null ? ClassSymbol.EMPTY_ARRAY :
|
||||
myMembers instanceof ClassSymbol ? new ClassSymbol[]{(ClassSymbol)myMembers} :
|
||||
(ClassSymbol[])myMembers;
|
||||
}
|
||||
|
||||
void setMembers(ClassSymbol[] members) {
|
||||
myMembers = members.length == 0 ? null : members.length == 1 ? members[0] : members;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -63,21 +63,23 @@ public class Symbols {
|
||||
return (ClassSymbol[])cs;
|
||||
}
|
||||
|
||||
public ClassSymbol enterClass(ClassAnchor classAnchor, int flags, int shortName, Symbol owner, UnitInfo info, QualifiedName[] supers) {
|
||||
QualifiedName qualifiedName = myNameEnvironment.qualifiedName(owner, shortName);
|
||||
public ClassSymbol enterClass(ClassAnchor classAnchor,
|
||||
int flags,
|
||||
int shortName,
|
||||
Symbol owner,
|
||||
UnitInfo info,
|
||||
QualifiedName[] supers,
|
||||
@Nullable QualifiedName qualifiedName) {
|
||||
StubClassAnchor stubClassAnchor = new StubClassAnchor(myClassSymbols.size(), classAnchor);
|
||||
ClassSymbol c = new ClassSymbol(stubClassAnchor, flags, owner, qualifiedName, shortName, info, supers);
|
||||
ClassSymbol c = new ClassSymbol(stubClassAnchor, flags, owner, shortName, info, supers);
|
||||
myClassSymbols.add(c);
|
||||
putClassByName(c);
|
||||
if (qualifiedName != null) {
|
||||
putClassByName(c, qualifiedName.myId);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
private void putClassByName(ClassSymbol classSymbol) {
|
||||
QualifiedName name = classSymbol.myQualifiedName;
|
||||
// anonymous class
|
||||
if (name == null)
|
||||
return;
|
||||
int nameId = name.myId;
|
||||
private void putClassByName(ClassSymbol classSymbol, int nameId) {
|
||||
ensureByNameCapacity(nameId);
|
||||
Object cs = myClassSymbolsByNameId[nameId];
|
||||
if (cs == null) {
|
||||
|
||||
@@ -43,7 +43,7 @@ public class IndexTree {
|
||||
public static final byte JAVA = 1;
|
||||
public static final byte GROOVY = 2;
|
||||
|
||||
private static int hashIdentifier(@Nullable String s) {
|
||||
public static int hashIdentifier(@Nullable String s) {
|
||||
if (s == null) return 0;
|
||||
|
||||
// not using String.hashCode because this way there's less collisions for short package names like 'com'
|
||||
|
||||
@@ -536,175 +536,184 @@ public class FindManagerImpl extends FindManager {
|
||||
int offset,
|
||||
@NotNull FindModel model,
|
||||
@NotNull final VirtualFile file) {
|
||||
FileType ftype = file.getFileType();
|
||||
Language lang = null;
|
||||
if (ftype instanceof LanguageFileType) {
|
||||
lang = ((LanguageFileType)ftype).getLanguage();
|
||||
}
|
||||
|
||||
CommentsLiteralsSearchData data = model.getUserData(ourCommentsLiteralsSearchDataKey);
|
||||
if (data == null || !Comparing.equal(data.lastFile, file) || !data.model.equals(model)) {
|
||||
SyntaxHighlighter highlighter = getHighlighter(file, lang);
|
||||
|
||||
if (highlighter == null) {
|
||||
// no syntax highlighter -> no search
|
||||
return NOT_FOUND_RESULT;
|
||||
synchronized (model) {
|
||||
FileType ftype = file.getFileType();
|
||||
Language lang = null;
|
||||
if (ftype instanceof LanguageFileType) {
|
||||
lang = ((LanguageFileType)ftype).getLanguage();
|
||||
}
|
||||
|
||||
TokenSet tokensOfInterest = TokenSet.EMPTY;
|
||||
Set<Language> relevantLanguages;
|
||||
if (lang != null) {
|
||||
final Language finalLang = lang;
|
||||
relevantLanguages = ApplicationManager.getApplication().runReadAction(new Computable<Set<Language>>() {
|
||||
@Override
|
||||
public Set<Language> compute() {
|
||||
THashSet<Language> result = new THashSet<Language>();
|
||||
CommentsLiteralsSearchData data = model.getUserData(ourCommentsLiteralsSearchDataKey);
|
||||
if (data == null || !Comparing.equal(data.lastFile, file) || !data.model.equals(model)) {
|
||||
SyntaxHighlighter highlighter = getHighlighter(file, lang);
|
||||
|
||||
FileViewProvider viewProvider = PsiManager.getInstance(myProject).findViewProvider(file);
|
||||
if (viewProvider != null) {
|
||||
result.addAll(viewProvider.getLanguages());
|
||||
}
|
||||
|
||||
if (result.isEmpty()) {
|
||||
result.add(finalLang);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
|
||||
for (Language relevantLanguage:relevantLanguages) {
|
||||
tokensOfInterest = addTokenTypesForLanguage(model, relevantLanguage, tokensOfInterest);
|
||||
if (highlighter == null) {
|
||||
// no syntax highlighter -> no search
|
||||
return NOT_FOUND_RESULT;
|
||||
}
|
||||
|
||||
if(model.isInStringLiteralsOnly()) {
|
||||
// TODO: xml does not have string literals defined so we add XmlAttributeValue element type as convenience
|
||||
final Lexer xmlLexer = getHighlighter(null, Language.findLanguageByID("XML")).getHighlightingLexer();
|
||||
final String marker = "xxx";
|
||||
xmlLexer.start("<a href=\"" + marker+ "\" />");
|
||||
TokenSet tokensOfInterest = TokenSet.EMPTY;
|
||||
Set<Language> relevantLanguages;
|
||||
if (lang != null) {
|
||||
final Language finalLang = lang;
|
||||
relevantLanguages = ApplicationManager.getApplication().runReadAction(new Computable<Set<Language>>() {
|
||||
@Override
|
||||
public Set<Language> compute() {
|
||||
THashSet<Language> result = new THashSet<Language>();
|
||||
|
||||
while (!marker.equals(xmlLexer.getTokenText())) {
|
||||
xmlLexer.advance();
|
||||
if (xmlLexer.getTokenType() == null) break;
|
||||
FileViewProvider viewProvider = PsiManager.getInstance(myProject).findViewProvider(file);
|
||||
if (viewProvider != null) {
|
||||
result.addAll(viewProvider.getLanguages());
|
||||
}
|
||||
|
||||
if (result.isEmpty()) {
|
||||
result.add(finalLang);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
|
||||
for (Language relevantLanguage : relevantLanguages) {
|
||||
tokensOfInterest = addTokenTypesForLanguage(model, relevantLanguage, tokensOfInterest);
|
||||
}
|
||||
|
||||
IElementType convenienceXmlAttrType = xmlLexer.getTokenType();
|
||||
if (convenienceXmlAttrType != null) {
|
||||
tokensOfInterest = TokenSet.orSet(tokensOfInterest, TokenSet.create(convenienceXmlAttrType));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
relevantLanguages = ContainerUtil.newHashSet();
|
||||
if (ftype instanceof AbstractFileType) {
|
||||
if (model.isInCommentsOnly()) {
|
||||
tokensOfInterest = TokenSet.create(CustomHighlighterTokenType.LINE_COMMENT, CustomHighlighterTokenType.MULTI_LINE_COMMENT);
|
||||
}
|
||||
if (model.isInStringLiteralsOnly()) {
|
||||
tokensOfInterest = TokenSet.orSet(tokensOfInterest, TokenSet.create(CustomHighlighterTokenType.STRING, CustomHighlighterTokenType.SINGLE_QUOTED_STRING));
|
||||
// TODO: xml does not have string literals defined so we add XmlAttributeValue element type as convenience
|
||||
final Lexer xmlLexer = getHighlighter(null, Language.findLanguageByID("XML")).getHighlightingLexer();
|
||||
final String marker = "xxx";
|
||||
xmlLexer.start("<a href=\"" + marker + "\" />");
|
||||
|
||||
while (!marker.equals(xmlLexer.getTokenText())) {
|
||||
xmlLexer.advance();
|
||||
if (xmlLexer.getTokenType() == null) break;
|
||||
}
|
||||
|
||||
IElementType convenienceXmlAttrType = xmlLexer.getTokenType();
|
||||
if (convenienceXmlAttrType != null) {
|
||||
tokensOfInterest = TokenSet.orSet(tokensOfInterest, TokenSet.create(convenienceXmlAttrType));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
relevantLanguages = ContainerUtil.newHashSet();
|
||||
if (ftype instanceof AbstractFileType) {
|
||||
if (model.isInCommentsOnly()) {
|
||||
tokensOfInterest = TokenSet.create(CustomHighlighterTokenType.LINE_COMMENT, CustomHighlighterTokenType.MULTI_LINE_COMMENT);
|
||||
}
|
||||
if (model.isInStringLiteralsOnly()) {
|
||||
tokensOfInterest = TokenSet.orSet(tokensOfInterest, TokenSet
|
||||
.create(CustomHighlighterTokenType.STRING, CustomHighlighterTokenType.SINGLE_QUOTED_STRING));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Matcher matcher = model.isRegularExpressions() ? compileRegExp(model, "") : null;
|
||||
StringSearcher searcher = matcher != null ? null : new StringSearcher(model.getStringToFind(), model.isCaseSensitive(), true);
|
||||
SyntaxHighlighterOverEditorHighlighter highlighterAdapter =
|
||||
new SyntaxHighlighterOverEditorHighlighter(highlighter, file, myProject);
|
||||
data =
|
||||
new CommentsLiteralsSearchData(file, relevantLanguages, highlighterAdapter, tokensOfInterest, searcher, matcher, model.clone());
|
||||
data.highlighter.restart(text);
|
||||
model.putUserData(ourCommentsLiteralsSearchDataKey, data);
|
||||
}
|
||||
|
||||
Matcher matcher = model.isRegularExpressions() ? compileRegExp(model, ""):null;
|
||||
StringSearcher searcher = matcher != null ? null: new StringSearcher(model.getStringToFind(), model.isCaseSensitive(), true);
|
||||
SyntaxHighlighterOverEditorHighlighter highlighterAdapter = new SyntaxHighlighterOverEditorHighlighter(highlighter, file, myProject);
|
||||
data = new CommentsLiteralsSearchData(file, relevantLanguages, highlighterAdapter, tokensOfInterest, searcher, matcher, model.clone());
|
||||
data.highlighter.restart(text);
|
||||
model.putUserData(ourCommentsLiteralsSearchDataKey, data);
|
||||
}
|
||||
int initialStartOffset = model.isForward() && data.startOffset < offset ? data.startOffset : 0;
|
||||
data.highlighter.resetPosition(initialStartOffset);
|
||||
final Lexer lexer = data.highlighter.getHighlightingLexer();
|
||||
|
||||
int initialStartOffset = model.isForward() && data.startOffset < offset ? data.startOffset : 0;
|
||||
data.highlighter.resetPosition(initialStartOffset);
|
||||
final Lexer lexer = data.highlighter.getHighlightingLexer();
|
||||
IElementType tokenType;
|
||||
TokenSet tokens = data.tokensOfInterest;
|
||||
|
||||
IElementType tokenType;
|
||||
TokenSet tokens = data.tokensOfInterest;
|
||||
int lastGoodOffset = 0;
|
||||
boolean scanningForward = model.isForward();
|
||||
FindResultImpl prevFindResult = NOT_FOUND_RESULT;
|
||||
|
||||
int lastGoodOffset = 0;
|
||||
boolean scanningForward = model.isForward();
|
||||
FindResultImpl prevFindResult = NOT_FOUND_RESULT;
|
||||
while ((tokenType = lexer.getTokenType()) != null) {
|
||||
if (lexer.getState() == 0) lastGoodOffset = lexer.getTokenStart();
|
||||
|
||||
while((tokenType = lexer.getTokenType()) != null) {
|
||||
if (lexer.getState() == 0) lastGoodOffset = lexer.getTokenStart();
|
||||
final TextAttributesKey[] keys = data.highlighter.getTokenHighlights(tokenType);
|
||||
|
||||
final TextAttributesKey[] keys = data.highlighter.getTokenHighlights(tokenType);
|
||||
|
||||
if (tokens.contains(tokenType) ||
|
||||
(model.isInStringLiteralsOnly() && ChunkExtractor.isHighlightedAsString(keys)) ||
|
||||
(model.isInCommentsOnly() && ChunkExtractor.isHighlightedAsComment(keys))
|
||||
) {
|
||||
int start = lexer.getTokenStart();
|
||||
int end = lexer.getTokenEnd();
|
||||
if (model.isInStringLiteralsOnly()) { // skip literal quotes itself from matching
|
||||
char c = text.charAt(start);
|
||||
if (c == '"' || c == '\'') {
|
||||
while (start < end && c == text.charAt(start)) {
|
||||
++start;
|
||||
if (c == text.charAt(end - 1) && start < end) --end;
|
||||
if (tokens.contains(tokenType) ||
|
||||
(model.isInStringLiteralsOnly() && ChunkExtractor.isHighlightedAsString(keys)) ||
|
||||
(model.isInCommentsOnly() && ChunkExtractor.isHighlightedAsComment(keys))
|
||||
) {
|
||||
int start = lexer.getTokenStart();
|
||||
int end = lexer.getTokenEnd();
|
||||
if (model.isInStringLiteralsOnly()) { // skip literal quotes itself from matching
|
||||
char c = text.charAt(start);
|
||||
if (c == '"' || c == '\'') {
|
||||
while (start < end && c == text.charAt(start)) {
|
||||
++start;
|
||||
if (c == text.charAt(end - 1) && start < end) --end;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while(true) {
|
||||
FindResultImpl findResult = null;
|
||||
while (true) {
|
||||
FindResultImpl findResult = null;
|
||||
|
||||
if (data.searcher != null) {
|
||||
int matchStart = data.searcher.scan(text, textArray, start, end);
|
||||
if (data.searcher != null) {
|
||||
int matchStart = data.searcher.scan(text, textArray, start, end);
|
||||
|
||||
if (matchStart != -1 && matchStart >= start) {
|
||||
final int matchEnd = matchStart + model.getStringToFind().length();
|
||||
if (matchStart >= offset || !scanningForward)
|
||||
findResult = new FindResultImpl(matchStart, matchEnd);
|
||||
else {
|
||||
start = matchEnd;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if (start <= end) {
|
||||
data.matcher.reset(StringPattern.newBombedCharSequence(text.subSequence(start, end)));
|
||||
if (data.matcher.find()) {
|
||||
final int matchEnd = start + data.matcher.end();
|
||||
int matchStart = start + data.matcher.start();
|
||||
if (matchStart >= offset || !scanningForward) {
|
||||
findResult = new FindResultImpl(matchStart, matchEnd);
|
||||
}
|
||||
else {
|
||||
int diff = 0;
|
||||
if (start == end) {
|
||||
diff = scanningForward ? 1:-1;
|
||||
if (matchStart != -1 && matchStart >= start) {
|
||||
final int matchEnd = matchStart + model.getStringToFind().length();
|
||||
if (matchStart >= offset || !scanningForward)
|
||||
findResult = new FindResultImpl(matchStart, matchEnd);
|
||||
else {
|
||||
start = matchEnd;
|
||||
continue;
|
||||
}
|
||||
start = matchEnd + diff;
|
||||
}
|
||||
}
|
||||
else if (start <= end) {
|
||||
data.matcher.reset(StringPattern.newBombedCharSequence(text.subSequence(start, end)));
|
||||
if (data.matcher.find()) {
|
||||
final int matchEnd = start + data.matcher.end();
|
||||
int matchStart = start + data.matcher.start();
|
||||
if (matchStart >= offset || !scanningForward) {
|
||||
findResult = new FindResultImpl(matchStart, matchEnd);
|
||||
}
|
||||
else {
|
||||
int diff = 0;
|
||||
if (start == end) {
|
||||
diff = scanningForward ? 1 : -1;
|
||||
}
|
||||
start = matchEnd + diff;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (findResult != null) {
|
||||
if (scanningForward) {
|
||||
data.startOffset = lastGoodOffset;
|
||||
return findResult;
|
||||
}
|
||||
else {
|
||||
|
||||
if (findResult.getEndOffset() >= offset) return prevFindResult;
|
||||
prevFindResult = findResult;
|
||||
start = findResult.getEndOffset();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (findResult != null) {
|
||||
if (scanningForward) {
|
||||
data.startOffset = lastGoodOffset;
|
||||
return findResult;
|
||||
} else {
|
||||
|
||||
if (findResult.getEndOffset() >= offset) return prevFindResult;
|
||||
prevFindResult = findResult;
|
||||
start = findResult.getEndOffset();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Language tokenLang = tokenType.getLanguage();
|
||||
if (tokenLang != lang && tokenLang != Language.ANY && !data.relevantLanguages.contains(tokenLang)) {
|
||||
tokens = addTokenTypesForLanguage(model, tokenLang, tokens);
|
||||
data.tokensOfInterest = tokens;
|
||||
data.relevantLanguages.add(tokenLang);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
Language tokenLang = tokenType.getLanguage();
|
||||
if (tokenLang != lang && tokenLang != Language.ANY && !data.relevantLanguages.contains(tokenLang)) {
|
||||
tokens = addTokenTypesForLanguage(model, tokenLang, tokens);
|
||||
data.tokensOfInterest = tokens;
|
||||
data.relevantLanguages.add(tokenLang);
|
||||
}
|
||||
|
||||
lexer.advance();
|
||||
}
|
||||
|
||||
lexer.advance();
|
||||
return prevFindResult;
|
||||
}
|
||||
|
||||
return prevFindResult;
|
||||
}
|
||||
|
||||
private static TokenSet addTokenTypesForLanguage(FindModel model, Language lang, TokenSet tokensOfInterest) {
|
||||
|
||||
Reference in New Issue
Block a user