diff --git a/ExtendedApi/src/com/intellij/extapi/psi/ASTWrapperPsiElement.java b/ExtendedApi/src/com/intellij/extapi/psi/ASTWrapperPsiElement.java index 6a895aea19bf..06ab7ec28c49 100644 --- a/ExtendedApi/src/com/intellij/extapi/psi/ASTWrapperPsiElement.java +++ b/ExtendedApi/src/com/intellij/extapi/psi/ASTWrapperPsiElement.java @@ -45,7 +45,8 @@ public class ASTWrapperPsiElement extends ElementBase implements PsiElement, Nav } public PsiManager getManager() { - return getParent().getManager(); + final PsiElement parent = getParent(); + return parent != null ? parent.getManager() : null; } public PsiElement[] getChildren() { @@ -235,7 +236,7 @@ public class ASTWrapperPsiElement extends ElementBase implements PsiElement, Nav } public SearchScope getUseScope() { - throw new UnsupportedOperationException(); + return getManager().getSearchHelper().getUseScope(this); } public ItemPresentation getPresentation() { diff --git a/openapi/src/com/intellij/lang/Language.java b/openapi/src/com/intellij/lang/Language.java index 70f1cbc79f33..1bcb8fe22066 100644 --- a/openapi/src/com/intellij/lang/Language.java +++ b/openapi/src/com/intellij/lang/Language.java @@ -1,10 +1,13 @@ package com.intellij.lang; import com.intellij.codeFormatting.PseudoTextBuilder; +import com.intellij.lang.cacheBuilder.WordsScanner; +import com.intellij.lang.folding.FoldingBuilder; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileTypes.PlainSyntaxHighlighter; import com.intellij.openapi.fileTypes.SyntaxHighlighter; import com.intellij.openapi.project.Project; +import com.intellij.psi.tree.IElementType; import java.util.HashMap; import java.util.Map; @@ -47,6 +50,18 @@ public abstract class Language { return null; } + public WordsScanner getWordsScanner() { + return null; + } + + public boolean mayHaveReferences(IElementType token) { + return false; + } + + public FoldingBuilder getFoldingBuilder() { + return null; + } + public String toString() { return "Language: " + myID; } diff --git a/openapi/src/com/intellij/lang/cacheBuilder/DefaultWordsScanner.java b/openapi/src/com/intellij/lang/cacheBuilder/DefaultWordsScanner.java new file mode 100644 index 000000000000..2c1f6b3aa7e9 --- /dev/null +++ b/openapi/src/com/intellij/lang/cacheBuilder/DefaultWordsScanner.java @@ -0,0 +1,82 @@ +package com.intellij.lang.cacheBuilder; + +import com.intellij.lexer.Lexer; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.tree.TokenSet; +import com.intellij.util.Processor; +import com.intellij.util.text.CharArrayCharSequence; +import com.intellij.util.text.CharArrayUtil; + +/** + * Created by IntelliJ IDEA. + * User: max + * Date: Jan 31, 2005 + * Time: 9:12:33 PM + * To change this template use File | Settings | File Templates. + */ +public class DefaultWordsScanner implements WordsScanner { + private Lexer myLexer; + private TokenSet myIdentifierTokenSet; + private TokenSet myCommentTokenSet; + private TokenSet myLiteralTokenSet; + + public DefaultWordsScanner(final Lexer lexer, final TokenSet identifierTokenSet, final TokenSet commentTokenSet, final TokenSet literalTokenSet) { + myLexer = lexer; + myIdentifierTokenSet = identifierTokenSet; + myCommentTokenSet = commentTokenSet; + myLiteralTokenSet = literalTokenSet; + } + + public void processWords(CharSequence fileText, Processor processor) { + char[] chars = CharArrayUtil.fromSequence(fileText); + myLexer.start(chars, 0, fileText.length()); + while (myLexer.getTokenType() != null) { + final IElementType type = myLexer.getTokenType(); + if (myIdentifierTokenSet.isInSet(type)) { + if (!processor.process(new WordOccurence(currentTokenText(chars), WordOccurence.Kind.CODE))) return; + } + else if (myCommentTokenSet.isInSet(type)) { + if (!stripWords(processor, currentTokenText(chars), WordOccurence.Kind.COMMENTS)) return; + } + else if (myLiteralTokenSet.isInSet(type)) { + if (!stripWords(processor, currentTokenText(chars), WordOccurence.Kind.LITERALS)) return; + } + myLexer.advance(); + } + } + + /** + * This code seems strange but it is more effective as Character.isJavaIdentifier_xxx_ quite hard operation due to unicode + */ + private static boolean stripWords(final Processor processor, + final CharArrayCharSequence tokenText, + final WordOccurence.Kind kind) { + int index = 0; + + ScanWordsLoop: + while(true){ + while(true){ + if (index == tokenText.length()) break ScanWordsLoop; + char c = tokenText.charAt(index); + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (Character.isJavaIdentifierStart(c) && c != '$')) break; + index++; + } + int index1 = index; + while(true){ + index++; + if (index == tokenText.length()) break; + char c = tokenText.charAt(index); + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) continue; + if (!Character.isJavaIdentifierPart(c) || c == '$') break; + } + if (index - index1 > 100) continue; // Strange limit but we should have some! + + if (!processor.process(new WordOccurence(tokenText.subSequence(index1, index), kind))) return false; + } + return true; + } + + private CharArrayCharSequence currentTokenText(final char[] chars) { + return new CharArrayCharSequence(chars, myLexer.getTokenStart(), myLexer.getTokenEnd()); + } +} diff --git a/openapi/src/com/intellij/lang/cacheBuilder/WordOccurence.java b/openapi/src/com/intellij/lang/cacheBuilder/WordOccurence.java new file mode 100644 index 000000000000..c2e70e83f6a7 --- /dev/null +++ b/openapi/src/com/intellij/lang/cacheBuilder/WordOccurence.java @@ -0,0 +1,42 @@ +package com.intellij.lang.cacheBuilder; + +/** + * Created by IntelliJ IDEA. + * User: max + * Date: Jan 31, 2005 + * Time: 9:09:06 PM + * To change this template use File | Settings | File Templates. + */ +public class WordOccurence { + private Kind myKind; + private CharSequence myText; + + public WordOccurence(final CharSequence text, final Kind kind) { + myKind = kind; + myText = text; + } + + public Kind getKind() { + return myKind; + } + + public CharSequence getText() { + return myText; + } + + public static class Kind { + public static final Kind CODE = new Kind("CODE"); + public static final Kind COMMENTS = new Kind("COMMENTS"); + public static final Kind LITERALS = new Kind("LITERALS"); + + private String myName; + + private Kind(String name){ + myName = name; + } + + public String toString() { + return "WordOccurence.Kind(" + myName + ")"; + } + } +} diff --git a/openapi/src/com/intellij/lang/cacheBuilder/WordsScanner.java b/openapi/src/com/intellij/lang/cacheBuilder/WordsScanner.java new file mode 100644 index 000000000000..925a900e729c --- /dev/null +++ b/openapi/src/com/intellij/lang/cacheBuilder/WordsScanner.java @@ -0,0 +1,14 @@ +package com.intellij.lang.cacheBuilder; + +import com.intellij.util.Processor; + +/** + * Created by IntelliJ IDEA. + * User: max + * Date: Jan 31, 2005 + * Time: 9:07:44 PM + * To change this template use File | Settings | File Templates. + */ +public interface WordsScanner { + void processWords(CharSequence fileText, Processor processor); +} diff --git a/openapi/src/com/intellij/lang/folding/FoldingBuilder.java b/openapi/src/com/intellij/lang/folding/FoldingBuilder.java new file mode 100644 index 000000000000..f78ee657eb40 --- /dev/null +++ b/openapi/src/com/intellij/lang/folding/FoldingBuilder.java @@ -0,0 +1,19 @@ +package com.intellij.lang.folding; + +import com.intellij.openapi.editor.Document; +import com.intellij.psi.PsiElement; + +/** + * Created by IntelliJ IDEA. + * User: max + * Date: Feb 1, 2005 + * Time: 12:10:19 AM + * To change this template use File | Settings | File Templates. + */ +public interface FoldingBuilder { + FoldingDescriptor[] buildFoldRegions(PsiElement file, Document document); + + String getPlaceholderText(PsiElement elt); + + boolean isCollapsedByDefault(PsiElement elt); +} diff --git a/openapi/src/com/intellij/lang/folding/FoldingDescriptor.java b/openapi/src/com/intellij/lang/folding/FoldingDescriptor.java new file mode 100644 index 000000000000..3d128e12e9d8 --- /dev/null +++ b/openapi/src/com/intellij/lang/folding/FoldingDescriptor.java @@ -0,0 +1,29 @@ +package com.intellij.lang.folding; + +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; + +/** + * Created by IntelliJ IDEA. + * User: max + * Date: Feb 1, 2005 + * Time: 12:14:17 AM + * To change this template use File | Settings | File Templates. + */ +public class FoldingDescriptor { + private PsiElement myElement; + private TextRange myRange; + + public FoldingDescriptor(final PsiElement element, final TextRange range) { + myElement = element; + myRange = range; + } + + public PsiElement getElement() { + return myElement; + } + + public TextRange getRange() { + return myRange; + } +} diff --git a/source/com/intellij/codeInsight/folding/impl/FoldingPolicy.java b/source/com/intellij/codeInsight/folding/impl/FoldingPolicy.java index 18c016fd548b..e895f03bf962 100644 --- a/source/com/intellij/codeInsight/folding/impl/FoldingPolicy.java +++ b/source/com/intellij/codeInsight/folding/impl/FoldingPolicy.java @@ -1,5 +1,9 @@ package com.intellij.codeInsight.folding.impl; +import com.intellij.lang.ASTNode; +import com.intellij.lang.Language; +import com.intellij.lang.folding.FoldingBuilder; +import com.intellij.lang.folding.FoldingDescriptor; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.progress.ProgressManager; @@ -10,7 +14,6 @@ import com.intellij.psi.javadoc.PsiDocComment; import com.intellij.psi.util.PropertyUtil; import com.intellij.psi.xml.*; import com.intellij.uiDesigner.compiler.CodeGenerator; -import com.intellij.lang.ASTNode; import java.util.*; @@ -28,6 +31,18 @@ class FoldingPolicy { element1.getTextRange().getEndOffset() - element.getTextRange().getEndOffset(); } }); + final Language lang = file.getLanguage(); + if (lang != null) { + final FoldingBuilder foldingBuilder = lang.getFoldingBuilder(); + if (foldingBuilder != null) { + final FoldingDescriptor[] foldingDescriptors = foldingBuilder.buildFoldRegions(file, document); + for (int i = 0; i < foldingDescriptors.length; i++) { + FoldingDescriptor descriptor = foldingDescriptors[i]; + map.put(descriptor.getElement(), descriptor.getRange()); + } + return map; + } + } if (file instanceof PsiJavaFile) { PsiImportList importList = ((PsiJavaFile) file).getImportList(); @@ -242,6 +257,14 @@ class FoldingPolicy { } public static String getFoldingText(PsiElement element) { + final Language lang = element.getLanguage(); + if (lang != null) { + final FoldingBuilder foldingBuilder = lang.getFoldingBuilder(); + if (foldingBuilder != null) { + return foldingBuilder.getPlaceholderText(element); + } + } + if (element instanceof PsiImportList) { return "..."; } else if ( element instanceof PsiMethod && CodeGenerator.SETUP_METHOD_NAME.equals(((PsiMethod) element).getName()) || @@ -264,6 +287,14 @@ class FoldingPolicy { public static boolean isCollapseByDefault(PsiElement element) { + final Language lang = element.getLanguage(); + if (lang != null) { + final FoldingBuilder foldingBuilder = lang.getFoldingBuilder(); + if (foldingBuilder != null) { + return foldingBuilder.isCollapsedByDefault(element); + } + } + CodeFoldingSettings settings = CodeFoldingSettings.getInstance(); if (element instanceof PsiImportList) { return settings.COLLAPSE_IMPORTS; diff --git a/source/com/intellij/psi/impl/cache/impl/idCache/IdTableBuilding.java b/source/com/intellij/psi/impl/cache/impl/idCache/IdTableBuilding.java index 0774227cff06..b91636de014d 100644 --- a/source/com/intellij/psi/impl/cache/impl/idCache/IdTableBuilding.java +++ b/source/com/intellij/psi/impl/cache/impl/idCache/IdTableBuilding.java @@ -4,6 +4,9 @@ import com.intellij.aspects.lexer._AspectjLexer; import com.intellij.aspects.psi.PsiAspectFile; import com.intellij.ide.startup.FileContent; import com.intellij.ide.todo.TodoConfiguration; +import com.intellij.lang.Language; +import com.intellij.lang.cacheBuilder.WordOccurence; +import com.intellij.lang.cacheBuilder.WordsScanner; import com.intellij.lexer.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileTypes.FileType; @@ -11,7 +14,6 @@ import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.util.text.StringUtil; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; -import com.intellij.psi.xml.XmlFile; import com.intellij.psi.impl.PsiManagerImpl; import com.intellij.psi.impl.cache.impl.CacheManagerImpl; import com.intellij.psi.impl.source.parsing.jsp.JspStep1Lexer; @@ -21,16 +23,18 @@ import com.intellij.psi.jsp.JspFile; import com.intellij.psi.search.TodoPattern; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.java.IJavaElementType; +import com.intellij.psi.xml.XmlFile; import com.intellij.uiDesigner.FormEditingUtil; import com.intellij.uiDesigner.compiler.Utils; import com.intellij.uiDesigner.lw.IComponent; import com.intellij.uiDesigner.lw.LwRootContainer; +import com.intellij.util.Processor; import com.intellij.util.text.CharArrayCharSequence; import gnu.trove.TIntIntHashMap; +import java.util.HashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.HashMap; public class IdTableBuilding { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.cache.impl.idCache.IdTableBuilding"); @@ -217,9 +221,41 @@ public class IdTableBuilding { return cacheBuilders.get(StdFileTypes.XML); } + final Language lang = psiFile.getLanguage(); + if (lang != null) { + final WordsScanner scanner = lang.getWordsScanner(); + if (scanner != null) { + return new WordsScannerIdCacheBuilderAdapter(scanner); + } + } + return null; } + private static class WordsScannerIdCacheBuilderAdapter implements IdCacheBuilder { + private WordsScanner myScanner; + + public WordsScannerIdCacheBuilderAdapter(final WordsScanner scanner) { + myScanner = scanner; + } + + public void build(char[] chars, int length, final TIntIntHashMap wordsTable, TodoPattern[] todoPatterns, int[] todoCounts) { + myScanner.processWords(new CharArrayCharSequence(chars, 0, length), new Processor() { + public boolean process(final WordOccurence t) { + IdCacheUtil.addOccurrence(wordsTable, t.getText().toString().hashCode(), convertToMask(t.getKind())); + return true; + } + + private int convertToMask(final WordOccurence.Kind kind) { + if (kind == WordOccurence.Kind.CODE) return WordInfo.IN_CODE; + if (kind == WordOccurence.Kind.COMMENTS) return WordInfo.IN_COMMENTS; + if (kind == WordOccurence.Kind.LITERALS) return WordInfo.IN_STRING_LITERALS; + return 0; + } + }); + } + } + public static Result getBuildingRunnable(PsiManagerImpl manager, FileContent fileContent, final boolean buildTodos) { if (LOG.isDebugEnabled()){ LOG.debug( diff --git a/source/com/intellij/psi/impl/search/LowLevelSearchUtil.java b/source/com/intellij/psi/impl/search/LowLevelSearchUtil.java index cb4124b974b6..07b2aaec3aee 100644 --- a/source/com/intellij/psi/impl/search/LowLevelSearchUtil.java +++ b/source/com/intellij/psi/impl/search/LowLevelSearchUtil.java @@ -1,5 +1,6 @@ package com.intellij.psi.impl.search; +import com.intellij.lang.ASTNode; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.psi.PsiElement; @@ -9,11 +10,9 @@ import com.intellij.psi.impl.source.parsing.ChameleonTransforming; import com.intellij.psi.impl.source.tree.ChameleonElement; import com.intellij.psi.impl.source.tree.CompositeElement; import com.intellij.psi.impl.source.tree.LeafElement; -import com.intellij.psi.impl.source.tree.TreeElement; import com.intellij.psi.search.PsiElementProcessor; import com.intellij.psi.tree.TokenSet; import com.intellij.util.text.StringSearcher; -import com.intellij.lang.ASTNode; import java.util.Set; @@ -33,8 +32,8 @@ public class LowLevelSearchUtil { TokenSet elementTypes, ProgressIndicator progress) { ProgressManager.getInstance().checkCanceled(); - - if (elementTypes.isInSet(scope.getElementType())) { + final PsiElement scopePsi = SourceTreeToPsiMap.treeElementToPsi(scope); + if (elementTypes.isInSet(scope.getElementType()) || scopePsi.getLanguage() != null && scopePsi.getLanguage().mayHaveReferences(scope.getElementType())) { int startOffset; int endOffset; if (scope instanceof LeafElement) { @@ -44,7 +43,7 @@ public class LowLevelSearchUtil { do { int i = leaf.searchWord(startOffset, searcher); if (i >= 0) { - if (!processor.execute(SourceTreeToPsiMap.treeElementToPsi(scope), i)) return false; + if (!processor.execute(scopePsi, i)) return false; startOffset = i + 1; } else { @@ -76,7 +75,7 @@ public class LowLevelSearchUtil { do { int i = searchWord(buffer, startOffset, endOffset, searcher); if (i >= 0) { - if (!processor.execute(SourceTreeToPsiMap.treeElementToPsi(scope), i - originalStartOffset)) return false; + if (!processor.execute(scopePsi, i - originalStartOffset)) return false; startOffset = i + 1; } else { @@ -99,40 +98,39 @@ public class LowLevelSearchUtil { PsiElementProcessorEx processor, TokenSet elementTypes, ProgressIndicator progress) { - synchronized (PsiLock.LOCK) { - ASTNode child = scope.getFirstChildNode(); - while (child != null) { - if (child instanceof ChameleonElement) { - LeafElement leaf = (LeafElement)child; - if (leaf.searchWord(0, searcher) >= 0) { - ASTNode next = child.getTreeNext(); - child = ChameleonTransforming.transform((ChameleonElement)child); - if (child == null) { - child = next; + synchronized (PsiLock.LOCK) { + ASTNode child = scope.getFirstChildNode(); + while (child != null) { + if (child instanceof ChameleonElement) { + LeafElement leaf = (LeafElement)child; + if (leaf.searchWord(0, searcher) >= 0) { + ASTNode next = child.getTreeNext(); + child = ChameleonTransforming.transform((ChameleonElement)child); + if (child == null) { + child = next; + } + continue; } - continue; } + child = child.getTreeNext(); } - child = child.getTreeNext(); } - } - ASTNode child = null; - while (true) { - synchronized (PsiLock.LOCK) { - child = child != null ? child.getTreeNext() : scope.getFirstChildNode(); - while (child instanceof ChameleonElement) { - child = child.getTreeNext(); - } - if (child == null) break; - } + ASTNode child = null; + while (true) { + synchronized (PsiLock.LOCK) { + child = child != null ? child.getTreeNext() : scope.getFirstChildNode(); + while (child instanceof ChameleonElement) { + child = child.getTreeNext(); + } + if (child == null) break; + } - if (!processElementsContainingWordInElement(processor, (TreeElement)child, searcher, elementTypes, - progress)) { - return false; - } - } - return true; + if (!processElementsContainingWordInElement(processor, child, searcher, elementTypes, progress)) { + return false; + } + } + return true; } public static int searchWord(char[] text, int startOffset, int endOffset, StringSearcher searcher) { @@ -164,8 +162,7 @@ public class LowLevelSearchUtil { ASTNode scope, TokenSet elementTypes, Set namesSet, - ProgressIndicator progress - ) { + ProgressIndicator progress) { ProgressManager.getInstance().checkCanceled(); if (elementTypes.isInSet(scope.getElementType())) {