From df752b5d8a72705a5853442784e24a4749fdd050 Mon Sep 17 00:00:00 2001 From: Maxim Shafirov Date: Sat, 22 Jan 2005 01:10:55 +0300 Subject: [PATCH] Draft PsiBuilder implementation. --- openapi/src/com/intellij/lang/ASTNode.java | 20 ++ .../com/intellij/lang/ParserDefinition.java | 2 + openapi/src/com/intellij/lang/PsiBuilder.java | 21 +- openapi/src/com/intellij/lang/PsiParser.java | 3 +- .../intellij/lang/impl/PsiBuilderImpl.java | 207 ++++++++++++++ .../psi/impl/source/ASTWrapperPsiElement.java | 262 ++++++++++++++++++ .../psi/impl/source/SourceTreeToPsiMap.java | 6 + .../impl/source/tree/CompositeElement.java | 153 +++++----- .../psi/impl/source/tree/LeafElement.java | 49 ++-- .../psi/impl/source/tree/LeafPsiElement.java | 2 +- .../psi/impl/source/tree/TreeElement.java | 106 +++---- 11 files changed, 683 insertions(+), 148 deletions(-) create mode 100644 source/com/intellij/lang/impl/PsiBuilderImpl.java create mode 100644 source/com/intellij/psi/impl/source/ASTWrapperPsiElement.java diff --git a/openapi/src/com/intellij/lang/ASTNode.java b/openapi/src/com/intellij/lang/ASTNode.java index 2692519ea164..105131285cf8 100644 --- a/openapi/src/com/intellij/lang/ASTNode.java +++ b/openapi/src/com/intellij/lang/ASTNode.java @@ -4,6 +4,7 @@ import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.UserDataHolder; +import com.intellij.openapi.util.Key; /** * Created by IntelliJ IDEA. @@ -16,27 +17,46 @@ public interface ASTNode extends UserDataHolder { IElementType getElementType(); String getText(); + boolean textContains(char c); int getStartOffset(); + int getTextLength(); + TextRange getTextRange(); ASTNode getTreeParent(); + ASTNode getFirstChildNode(); + ASTNode getLastChildNode(); + ASTNode getTreeNext(); + ASTNode getTreePrev(); ASTNode[] getChildren(TokenSet filter); + void addChild(ASTNode child); + void addChild(ASTNode child, ASTNode anchorBefore); + void removeChild(ASTNode child); + void replaceChild(ASTNode oldChild, ASTNode newChild); + void replaceAllChildrenToChildrenOf(ASTNode anotherParent); + void addChildren(ASTNode firstChild, ASTNode lastChild, ASTNode anchorBefore); Object clone(); ASTNode copyElement(); + + ASTNode findLeafElementAt(int offset); + + T getCopyableUserData(Key key); + + void putCopyableUserData(Key key, T value); } diff --git a/openapi/src/com/intellij/lang/ParserDefinition.java b/openapi/src/com/intellij/lang/ParserDefinition.java index fbe07cc0d172..fd55b01e325c 100644 --- a/openapi/src/com/intellij/lang/ParserDefinition.java +++ b/openapi/src/com/intellij/lang/ParserDefinition.java @@ -13,6 +13,8 @@ import com.intellij.psi.tree.IElementType; */ public interface ParserDefinition { Lexer createLexer(); + PsiParser createParser(); + PsiElement createElement(ASTNode node); } diff --git a/openapi/src/com/intellij/lang/PsiBuilder.java b/openapi/src/com/intellij/lang/PsiBuilder.java index 905a7205ccae..3c6b1e042505 100644 --- a/openapi/src/com/intellij/lang/PsiBuilder.java +++ b/openapi/src/com/intellij/lang/PsiBuilder.java @@ -11,13 +11,30 @@ import com.intellij.psi.PsiElement; * To change this template use File | Settings | File Templates. */ public interface PsiBuilder { - interface Marker {} + interface Marker { + } + + interface Lexem { + IElementType getTokenType(); + + String getTokenText(); + } + + void advanceLexer(); + + Lexem getCurrentLexem(); Marker start(IElementType symbol); + void rollbackTo(Marker marker); + void drop(Marker marker); + void done(Marker marker); + void insertErrorElement(String messageText); - PsiElement getTreeBuilt(); + boolean eof(); + + ASTNode getTreeBuilt(); } diff --git a/openapi/src/com/intellij/lang/PsiParser.java b/openapi/src/com/intellij/lang/PsiParser.java index 11ac861ad861..8487f6ea02e8 100644 --- a/openapi/src/com/intellij/lang/PsiParser.java +++ b/openapi/src/com/intellij/lang/PsiParser.java @@ -1,6 +1,7 @@ package com.intellij.lang; import com.intellij.psi.PsiElement; +import com.intellij.psi.tree.IElementType; import com.intellij.lexer.Lexer; /** @@ -11,5 +12,5 @@ import com.intellij.lexer.Lexer; * To change this template use File | Settings | File Templates. */ public interface PsiParser { - PsiElement parse(Lexer lexer, PsiBuilder builder); + PsiElement parse(IElementType root, Lexer lexer, PsiBuilder builder); } diff --git a/source/com/intellij/lang/impl/PsiBuilderImpl.java b/source/com/intellij/lang/impl/PsiBuilderImpl.java new file mode 100644 index 000000000000..3ea175c5d241 --- /dev/null +++ b/source/com/intellij/lang/impl/PsiBuilderImpl.java @@ -0,0 +1,207 @@ +package com.intellij.lang.impl; + +import com.intellij.lang.ASTNode; +import com.intellij.lang.PsiBuilder; +import com.intellij.lexer.Lexer; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.psi.impl.source.tree.*; +import com.intellij.psi.tree.IElementType; +import com.intellij.util.CharTable; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by IntelliJ IDEA. + * User: max + * Date: Jan 21, 2005 + * Time: 3:30:29 PM + * To change this template use File | Settings | File Templates. + */ +public class PsiBuilderImpl implements PsiBuilder { + private static final Logger LOG = Logger.getInstance("#com.intellij.lang.impl.PsiBuilderImpl"); + + private List myLexems = new ArrayList(); + private List myProduction = new ArrayList(); + + private Lexer myLexer; + private int myCurrentLexem; + private CharTable myCharTable; + private boolean myStarted = false; + + public PsiBuilderImpl(final Lexer lexer, final CharTable charTable) { + myCharTable = charTable; + myLexer = lexer; + } + + private static class MarkerImpl implements Marker { + public IElementType myType; + public int myLexemIndex; + public DoneMarker myDoneMarker = null; + + public MarkerImpl(final IElementType type, int idx) { + myType = type; + myLexemIndex = idx; + } + } + + private class LexemImpl implements Lexem { + private IElementType myTokenType; + private int myTokenStart; + private int myTokenEnd; + private int myState; + + public LexemImpl() { + myTokenType = myLexer.getTokenType(); + myTokenStart = myLexer.getTokenStart(); + myTokenEnd = myLexer.getTokenEnd(); + myState = myLexer.getState(); + } + + public IElementType getTokenType() { + return myTokenType; + } + + public String getTokenText() { + return new String(myLexer.getBuffer(), myTokenStart, myTokenEnd - myTokenStart); + } + } + + private static class DoneMarker { + public MarkerImpl myMarker; + + public DoneMarker(final MarkerImpl marker) { + myMarker = marker; + } + } + + private static class ErrorItem { + String myMessage; + + public ErrorItem(final String message) { + myMessage = message; + } + } + + public void advanceLexer() { + myProduction.add(getCurrentLexem()); + myCurrentLexem++; + if (myCurrentLexem > myLexems.size()) { + LOG.assertTrue(!eof()); + final Lexem lxm = advanceOriginalLexer(); + if (lxm == null) return; + myLexems.add(lxm); + } + } + + private Lexem advanceOriginalLexer() { + myLexer.advance(); + return myLexer.getTokenType() == null ? null : new LexemImpl(); + } + + + public Lexem getCurrentLexem() { + return myLexems.get(myCurrentLexem); + } + + public Marker start(IElementType symbol) { + if (!myStarted) { + myStarted = true; + myLexems.add(new LexemImpl()); + } + + MarkerImpl marker = new MarkerImpl(symbol, myCurrentLexem); + myProduction.add(marker); + return marker; + } + + public boolean eof() { + if (myCurrentLexem + 1 < myLexems.size()) return false; + if (myLexer.getTokenType() == null) return true; + final Lexem lxm = advanceOriginalLexer(); + if (lxm == null) return true; + myLexems.add(lxm); + return false; + } + + public void rollbackTo(Marker marker) { + myCurrentLexem = ((MarkerImpl)marker).myLexemIndex; + int idx = myProduction.lastIndexOf(marker); + + LOG.assertTrue(idx >= 0, "The marker must be added before rolled back to."); + for (int i = myProduction.size() - 1; i >= idx; i--) { + myProduction.remove(i); + } + } + + public void drop(Marker marker) { + final boolean removed = myProduction.remove(marker); + LOG.assertTrue(removed, "The marker must be added before it is dropped."); + } + + public void done(Marker marker) { + LOG.assertTrue(((MarkerImpl)marker).myDoneMarker == null, "Marker already done."); + int idx = myProduction.lastIndexOf(marker); + LOG.assertTrue(idx >= 0, "Marker never been added."); + + for (int i = myProduction.size() - 1; i > idx; i--) { + Object item = myProduction.get(i); + if (item instanceof Marker) { + MarkerImpl otherMarker = (MarkerImpl)item; + if (otherMarker.myDoneMarker == null) { + LOG.error("Another not done marker of type [" + otherMarker.myType + "] added after this one. Must be done before this."); + } + } + } + + DoneMarker doneMarker = new DoneMarker((MarkerImpl)marker); + ((MarkerImpl)marker).myDoneMarker = doneMarker; + myProduction.add(doneMarker); + } + + public void insertErrorElement(String messageText) { + myProduction.add(new ErrorItem(messageText)); + } + + public ASTNode getTreeBuilt() { + MarkerImpl rootMarker = (MarkerImpl)myProduction.get(0); + ASTNode rootNode = new CompositeElement(rootMarker.myType); + rootNode.putUserData(CharTable.CHAR_TABLE_KEY, myCharTable); + + ASTNode curNode = rootNode; + for (int i = 1; i < myProduction.size(); i++) { + LOG.assertTrue(curNode != null, "Unexpected end of the production"); + Object item = myProduction.get(i); + if (item instanceof LexemImpl) { + LexemImpl lexem = (LexemImpl)item; + final LeafPsiElement childNode = new LeafPsiElement(lexem.getTokenType(), + myLexer.getBuffer(), + lexem.myTokenStart, + lexem.myTokenEnd, + lexem.myState, + myCharTable); + TreeUtil.addChildren((CompositeElement)curNode, childNode); + } + else if (item instanceof MarkerImpl) { + MarkerImpl marker = (MarkerImpl)item; + ASTNode childNode = new CompositeElement(marker.myType); + TreeUtil.addChildren((CompositeElement)curNode, (TreeElement)childNode); + curNode = childNode; + } + else if (item instanceof DoneMarker) { + DoneMarker doneMarker = (DoneMarker)item; + LOG.assertTrue(doneMarker.myMarker.myType == curNode.getElementType()); + curNode = curNode.getTreeParent(); + } + else if (item instanceof ErrorItem) { + final PsiErrorElementImpl errorElement = new PsiErrorElementImpl(); + errorElement.setErrorDescription(((ErrorItem)item).myMessage); + TreeUtil.addChildren((CompositeElement)curNode, errorElement); + } + } + + LOG.assertTrue(curNode == null, "Unbalanced tree"); + + return rootNode; + } +} diff --git a/source/com/intellij/psi/impl/source/ASTWrapperPsiElement.java b/source/com/intellij/psi/impl/source/ASTWrapperPsiElement.java new file mode 100644 index 000000000000..3642f286e05e --- /dev/null +++ b/source/com/intellij/psi/impl/source/ASTWrapperPsiElement.java @@ -0,0 +1,262 @@ +package com.intellij.psi.impl.source; + +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.scope.PsiScopeProcessor; +import com.intellij.psi.impl.ElementBase; +import com.intellij.psi.impl.SharedPsiElementImplUtil; +import com.intellij.lang.ASTNode; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.vcs.FileStatus; +import com.intellij.openapi.vcs.FileStatusManager; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.IncorrectOperationException; +import com.intellij.navigation.ItemPresentation; +import com.intellij.navigation.NavigationItem; +import com.intellij.ide.util.EditSourceUtil; + +/** + * Created by IntelliJ IDEA. + * User: max + * Date: Jan 20, 2005 + * Time: 2:54:30 PM + * To change this template use File | Settings | File Templates. + */ +public class ASTWrapperPsiElement extends ElementBase implements PsiElement, NavigationItem { + private ASTNode myNode; + + public ASTWrapperPsiElement(final ASTNode node) { + myNode = node; + } + + public Project getProject() { + final PsiManager manager = getManager(); + return manager != null ? manager.getProject() : null; + } + + public PsiManager getManager() { + return getParent().getManager(); + } + + public PsiElement[] getChildren() { + return new PsiElement[0]; + } + + public PsiElement getParent() { + return SourceTreeToPsiMap.treeElementToPsi(myNode.getTreeParent()); + } + + public PsiElement getFirstChild() { + return SourceTreeToPsiMap.treeElementToPsi(myNode.getFirstChildNode()); + } + + public PsiElement getLastChild() { + return SourceTreeToPsiMap.treeElementToPsi(myNode.getLastChildNode()); + } + + public PsiElement getNextSibling() { + return SourceTreeToPsiMap.treeElementToPsi(myNode.getTreeNext()); + } + + public PsiElement getPrevSibling() { + return SourceTreeToPsiMap.treeElementToPsi(myNode.getTreePrev()); + } + + public PsiFile getContainingFile() { + return getParent().getContainingFile(); + } + + public TextRange getTextRange() { + return myNode.getTextRange(); + } + + public int getStartOffsetInParent() { + return myNode.getStartOffset() - myNode.getTreeParent().getStartOffset(); + } + + public int getTextLength() { + return myNode.getTextLength(); + } + + public PsiElement findElementAt(int offset) { + ASTNode treeElement = myNode.findLeafElementAt(offset); + return SourceTreeToPsiMap.treeElementToPsi(treeElement); + } + + public PsiReference findReferenceAt(int offset) { + return SharedPsiElementImplUtil.findReferenceAt(this, offset); + } + + public int getTextOffset() { + return myNode.getStartOffset(); + } + + public String getText() { + return myNode.getText(); + } + + public char[] textToCharArray() { + return myNode.getText().toCharArray(); + } + + public PsiElement getNavigationElement() { + return this; + } + + public PsiElement getOriginalElement() { + return this; + } + + //Q: get rid of these methods? + public boolean textMatches(CharSequence text) { + throw new UnsupportedOperationException(); + } + + public boolean textMatches(PsiElement element) { + throw new UnsupportedOperationException(); + } + + public boolean textContains(char c) { + return myNode.textContains(c); + } + + public void accept(PsiElementVisitor visitor) { + visitor.visitElement(this); + } + + public void acceptChildren(PsiElementVisitor visitor) { + ASTNode child = myNode.getFirstChildNode(); + while (child != null) { + SourceTreeToPsiMap.treeElementToPsi(child).accept(visitor); + child = child.getTreeNext(); + } + } + + public PsiElement copy() { + return (PsiElement)clone(); + } + + public PsiElement add(PsiElement element) throws IncorrectOperationException { + + throw new UnsupportedOperationException(); + } + + public PsiElement addBefore(PsiElement element, PsiElement anchor) throws IncorrectOperationException { + throw new UnsupportedOperationException(); + } + + public PsiElement addAfter(PsiElement element, PsiElement anchor) throws IncorrectOperationException { + throw new UnsupportedOperationException(); + } + + public void checkAdd(PsiElement element) throws IncorrectOperationException { + throw new UnsupportedOperationException(); + } + + public PsiElement addRange(PsiElement first, PsiElement last) throws IncorrectOperationException { + throw new UnsupportedOperationException(); + } + + public PsiElement addRangeBefore(PsiElement first, PsiElement last, PsiElement anchor) throws IncorrectOperationException { + throw new UnsupportedOperationException(); + } + + public PsiElement addRangeAfter(PsiElement first, PsiElement last, PsiElement anchor) throws IncorrectOperationException { + throw new UnsupportedOperationException(); + } + + public void delete() throws IncorrectOperationException { + throw new UnsupportedOperationException(); + } + + public void checkDelete() throws IncorrectOperationException { + throw new UnsupportedOperationException(); + } + + public void deleteChildRange(PsiElement first, PsiElement last) throws IncorrectOperationException { + throw new UnsupportedOperationException(); + } + + public PsiElement replace(PsiElement newElement) throws IncorrectOperationException { + throw new UnsupportedOperationException(); + } + + public boolean isValid() { + return getManager() != null; + } + + public boolean isWritable() { + return getContainingFile().isWritable(); + } + + public PsiReference getReference() { + return null; + } + + public PsiReference[] getReferences() { + return SharedPsiElementImplUtil.getReferences(this); + } + + public boolean processDeclarations(PsiScopeProcessor processor, + PsiSubstitutor substitutor, + PsiElement lastParent, + PsiElement place) { + return true; + } + + public PsiElement getContext() { + throw new UnsupportedOperationException(); + } + + public boolean isPhysical() { + return getContainingFile().isPhysical(); + } + + public GlobalSearchScope getResolveScope() { + throw new UnsupportedOperationException(); + } + + public GlobalSearchScope getUseScope() { + throw new UnsupportedOperationException(); + } + + public ItemPresentation getPresentation() { + return null; + } + + public String getName() { + return null; + } + + public void navigate(boolean requestFocus) { + if (canNavigate()) { + EditSourceUtil.getDescriptor(getNavigationElement()).navigate(requestFocus); + } + } + + public boolean canNavigate() { + return true; + } + + public FileStatus getFileStatus() { + if (!isPhysical()) return FileStatus.NOT_CHANGED; + PsiFile contFile = getContainingFile(); + if (contFile == null) return FileStatus.NOT_CHANGED; + VirtualFile vFile = contFile.getVirtualFile(); + return vFile != null ? FileStatusManager.getInstance(getProject()).getStatus(vFile) : FileStatus.NOT_CHANGED; + } + + public T getCopyableUserData(Key key) { + return myNode.getCopyableUserData(key); + } + + public void putCopyableUserData(Key key, T value) { + myNode.putCopyableUserData(key, value); + } + + public ASTNode getNode() { + return myNode; + } +} diff --git a/source/com/intellij/psi/impl/source/SourceTreeToPsiMap.java b/source/com/intellij/psi/impl/source/SourceTreeToPsiMap.java index 64a8cf79a89c..87948841502f 100644 --- a/source/com/intellij/psi/impl/source/SourceTreeToPsiMap.java +++ b/source/com/intellij/psi/impl/source/SourceTreeToPsiMap.java @@ -23,6 +23,9 @@ public class SourceTreeToPsiMap { return RepositoryElementsManager.getOrFindPsiElement((RepositoryTreeElement)element); } else { + PsiElement psiElement = element.getUserData(TreeElement.PSI_ELEMENT_KEY); + if (psiElement != null) return psiElement; + LOG.error("Not PsiElement:" + element); return null; } @@ -39,6 +42,9 @@ public class SourceTreeToPsiMap { else if (psiElement instanceof SrcRepositoryPsiElement) { return RepositoryElementsManager.getOrFindTreeElement((SrcRepositoryPsiElement)psiElement); } + else if (psiElement instanceof ASTWrapperPsiElement) { + return ((ASTWrapperPsiElement)psiElement).getNode(); + } else { LOG.error("Not TreeElement:" + psiElement); return null; diff --git a/source/com/intellij/psi/impl/source/tree/CompositeElement.java b/source/com/intellij/psi/impl/source/tree/CompositeElement.java index ab5cb1b9343e..0f6b4d55a47e 100644 --- a/source/com/intellij/psi/impl/source/tree/CompositeElement.java +++ b/source/com/intellij/psi/impl/source/tree/CompositeElement.java @@ -14,7 +14,7 @@ import com.intellij.psi.tree.TokenSet; import com.intellij.util.CharTable; import com.intellij.lang.ASTNode; -public class CompositeElement extends TreeElement implements Cloneable{ +public class CompositeElement extends TreeElement implements Cloneable { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.CompositeElement"); public TreeElement prev = null; @@ -30,51 +30,56 @@ public class CompositeElement extends TreeElement implements Cloneable{ this.type = type; } - public int getModificationCount(){ + public int getModificationCount() { return myModificationsCount; } public int getStartOffset() { CompositeElement parent = this; int sum = 0; - while(parent != null){ + while (parent != null) { sum += parent.getModificationCount(); parent = parent.getTreeParent(); } - if(sum != myParentModifications){ + if (sum != myParentModifications) { myParentModifications = sum; - if(prev != null) myStartOffset = prev.getStartOffset() + prev.getTextLength(); + if (prev != null) { + myStartOffset = prev.getStartOffset() + prev.getTextLength(); + } else { - if(this.parent != null) + if (this.parent != null) { myStartOffset = this.parent.getStartOffset(); - else myStartOffset = 0; + } + else { + myStartOffset = 0; + } } } return myStartOffset; } public Object clone() { - CompositeElement clone = (CompositeElement)super.clone(); + CompositeElement clone = (CompositeElement)super.clone(); - synchronized (PsiLock.LOCK) { - clone.clearCaches(); - clone.parent = null; - clone.prev = null; - clone.firstChild = null; - clone.lastChild = null; - clone.myModificationsCount = 0; - clone.myParentModifications = -1; - for (ASTNode child = getFirstChildNode(); child != null; child = child.getTreeNext()) { - TreeUtil.addChildren(clone, (TreeElement)child.clone()); - } + synchronized (PsiLock.LOCK) { + clone.clearCaches(); + clone.parent = null; + clone.prev = null; + clone.firstChild = null; + clone.lastChild = null; + clone.myModificationsCount = 0; + clone.myParentModifications = -1; + for (ASTNode child = getFirstChildNode(); child != null; child = child.getTreeNext()) { + TreeUtil.addChildren(clone, (TreeElement)child.clone()); } - return clone; + } + return clone; } public void subtreeChanged() { clearCaches(); CompositeElement treeParent = getTreeParent(); - if(treeParent != null) treeParent.subtreeChanged(); + if (treeParent != null) treeParent.subtreeChanged(); } public void clearCaches() { @@ -108,23 +113,23 @@ public class CompositeElement extends TreeElement implements Cloneable{ public LeafElement findLeafElementAt(int offset) { - synchronized (PsiLock.LOCK) { - final CharTable table = SharedImplUtil.findCharTableByTree(this); - TreeElement child = firstChild; - while (child != null) { - final int textLength = child.getTextLength(table); - if (textLength > offset) { - if (child instanceof ChameleonElement) { - child = ChameleonTransforming.transform((ChameleonElement)child); - continue; - } - return child.findLeafElementAt(offset); + synchronized (PsiLock.LOCK) { + final CharTable table = SharedImplUtil.findCharTableByTree(this); + TreeElement child = firstChild; + while (child != null) { + final int textLength = child.getTextLength(table); + if (textLength > offset) { + if (child instanceof ChameleonElement) { + child = ChameleonTransforming.transform((ChameleonElement)child); + continue; } - offset -= textLength; - child = child.getTreeNext(); + return child.findLeafElementAt(offset); } - return null; + offset -= textLength; + child = child.getTreeNext(); } + return null; + } } public String getText() { @@ -132,21 +137,21 @@ public class CompositeElement extends TreeElement implements Cloneable{ } public String getText(CharTable table) { - synchronized (PsiLock.LOCK) { - // check if all elements are laid out consequently in the same buffer (optimization): - char[] buffer = new char[getTextLength()]; - SourceUtil.toBuffer(this, buffer, 0, table); -//return StringFactory.createStringFromConstantArray(buffer); - return new String(buffer); - } + synchronized (PsiLock.LOCK) { + // check if all elements are laid out consequently in the same buffer (optimization): + char[] buffer = new char[getTextLength()]; + SourceUtil.toBuffer(this, buffer, 0, table); + //return StringFactory.createStringFromConstantArray(buffer); + return new String(buffer); + } } public char[] textToCharArray() { - synchronized (PsiLock.LOCK) { - char[] buffer = new char[getTextLength()]; - SourceUtil.toBuffer(this, buffer, 0); - return buffer; - } + synchronized (PsiLock.LOCK) { + char[] buffer = new char[getTextLength()]; + SourceUtil.toBuffer(this, buffer, 0); + return buffer; + } } public boolean textContains(char c) { @@ -160,13 +165,13 @@ public class CompositeElement extends TreeElement implements Cloneable{ } public ASTNode findChildByRole(int role) { - LOG.assertTrue(ChildRole.isUnique(role)); - synchronized (PsiLock.LOCK) { - for (ASTNode child = getFirstChildNode(); child != null; child = child.getTreeNext()) { - if (getChildRole(child) == role) return child; - } + LOG.assertTrue(ChildRole.isUnique(role)); + synchronized (PsiLock.LOCK) { + for (ASTNode child = getFirstChildNode(); child != null; child = child.getTreeNext()) { + if (getChildRole(child) == role) return child; } - return null; + } + return null; } public int getChildRole(ASTNode child) { @@ -175,10 +180,10 @@ public class CompositeElement extends TreeElement implements Cloneable{ } protected final int getChildRole(ASTNode child, int roleCandidate) { - if (findChildByRole(roleCandidate) == child){ + if (findChildByRole(roleCandidate) == child) { return roleCandidate; } - else{ + else { return ChildRole.NONE; } } @@ -190,8 +195,8 @@ public class CompositeElement extends TreeElement implements Cloneable{ } final ASTNode[] result = new ASTNode[count]; count = 0; - for(ASTNode child = getFirstChildNode(); child != null; child = child.getTreeNext()){ - if (filter == null || filter.isInSet(child.getElementType())){ + for (ASTNode child = getFirstChildNode(); child != null; child = child.getTreeNext()) { + if (filter == null || filter.isInSet(child.getElementType())) { result[count++] = child; } } @@ -204,14 +209,14 @@ public class CompositeElement extends TreeElement implements Cloneable{ int count = countChildren(filter); - if (count == 0){ + if (count == 0) { return constructor.newPsiElementArray(0); } PsiElement[] result = constructor.newPsiElementArray(count); count = 0; - for(ASTNode child = getFirstChildNode(); child != null; child = child.getTreeNext()){ - if (filter == null || filter.isInSet(child.getElementType())){ + for (ASTNode child = getFirstChildNode(); child != null; child = child.getTreeNext()) { + if (filter == null || filter.isInSet(child.getElementType())) { result[count++] = SourceTreeToPsiMap.treeElementToPsi(child); } } @@ -223,7 +228,7 @@ public class CompositeElement extends TreeElement implements Cloneable{ // no lock is needed because all chameleons are expanded already int count = 0; - for(ASTNode child = getFirstChildNode(); child != null; child = child.getTreeNext()){ + for (ASTNode child = getFirstChildNode(); child != null; child = child.getTreeNext()) { if (filter == null || filter.isInSet(child.getElementType())) { count++; } @@ -237,14 +242,14 @@ public class CompositeElement extends TreeElement implements Cloneable{ */ public TreeElement addInternal(TreeElement first, ASTNode last, ASTNode anchor, Boolean before) { ASTNode anchorBefore; - if (anchor != null){ + if (anchor != null) { anchorBefore = before.booleanValue() ? anchor : anchor.getTreeNext(); } - else{ - if (before != null && !before.booleanValue()){ + else { + if (before != null && !before.booleanValue()) { anchorBefore = firstChild; } - else{ + else { anchorBefore = null; } } @@ -268,7 +273,7 @@ public class CompositeElement extends TreeElement implements Cloneable{ private int myCachedLength = -1; public int getTextLength() { - if (myCachedLength < 0){ + if (myCachedLength < 0) { myCachedLength = getLengthInner(SharedImplUtil.findCharTableByTree(this)); } if (DebugUtil.CHECK) { @@ -286,13 +291,13 @@ public class CompositeElement extends TreeElement implements Cloneable{ } protected int getLengthInner(CharTable charTableByTree) { - synchronized (PsiLock.LOCK) { - int length = 0; - for (TreeElement child = firstChild; child != null; child = child.getTreeNext()) { - length += child.getTextLength(charTableByTree); - } - return length; + synchronized (PsiLock.LOCK) { + int length = 0; + for (TreeElement child = firstChild; child != null; child = child.getTreeNext()) { + length += child.getTextLength(charTableByTree); } + return length; + } } public void setCachedLength(int length) { @@ -311,6 +316,10 @@ public class CompositeElement extends TreeElement implements Cloneable{ ChangeUtil.addChild(this, (TreeElement)child, (TreeElement)anchorBefore); } + public void addChild(ASTNode child) { + ChangeUtil.addChild(this, (TreeElement)child, null); + } + public void removeChild(ASTNode child) { ChangeUtil.removeChild(this, (TreeElement)child); } diff --git a/source/com/intellij/psi/impl/source/tree/LeafElement.java b/source/com/intellij/psi/impl/source/tree/LeafElement.java index 8633f3ba983b..a92f39a99653 100644 --- a/source/com/intellij/psi/impl/source/tree/LeafElement.java +++ b/source/com/intellij/psi/impl/source/tree/LeafElement.java @@ -10,15 +10,18 @@ import sun.plugin.dom.exception.InvalidStateException; public abstract class LeafElement extends TreeElement { private volatile int myState = 0; // 16 bit for type, 15 bit for state and 1 bit for parentFlag + public abstract char charAt(int position); + public abstract int searchWord(int startOffset, StringSearcher searcher); + public abstract int copyTo(char[] buffer, int start); - public int copyTo(char[] buffer, int start, CharTable table){ + public int copyTo(char[] buffer, int start, CharTable table) { return copyTo(buffer, start); } - public int getTextLength(CharTable table){ + public int getTextLength(CharTable table) { return getTextLength(); } @@ -36,10 +39,10 @@ public abstract class LeafElement extends TreeElement { public abstract int textMatches(CharSequence buffer, int start); - public void registerInCharTable(CharTable table, CharTable oldCharTab) {} + public void registerInCharTable(CharTable table, CharTable oldCharTab) { } //boolean isLast = false; - public synchronized void setState(int state){ + public synchronized void setState(int state) { myState = myState & 0x8000FFFF | (((state + 1) & 0x7FFF) << 16); } @@ -49,58 +52,60 @@ public abstract class LeafElement extends TreeElement { return IElementType.find(sh); } - public int getState(){ + public int getState() { return (myState >> 16) & 0x7FFF - 1; } - private boolean isLast(){ + private boolean isLast() { return (myState & 0x80000000) != 0; } - private void setLast(boolean last){ - if(last) + private void setLast(boolean last) { + if (last) { myState |= 0x80000000; - else + } + else { myState &= 0x7FFFFFFF; + } } public synchronized TreeElement getTreeNext() { - if(!isLast()) return next; + if (!isLast()) return next; return null; } public synchronized CompositeElement getTreeParent() { - if(isLast()) return (CompositeElement)next; + if (isLast()) return (CompositeElement)next; TreeElement next = this.next; - while(next instanceof LeafElement && !((LeafElement)next).isLast()) next = next.next; - if(next != null) return next.getTreeParent(); + while (next instanceof LeafElement && !((LeafElement)next).isLast()) next = next.next; + if (next != null) return next.getTreeParent(); return null; } public synchronized TreeElement getTreePrev() { final CompositeElement parent = getTreeParent(); - if(parent == null) return null; + if (parent == null) return null; TreeElement firstChild = parent.firstChild; - if(firstChild == this) return null; - while(firstChild != null && firstChild.next != this) firstChild = firstChild.next; + if (firstChild == this) return null; + while (firstChild != null && firstChild.next != this) firstChild = firstChild.next; return firstChild; } public synchronized void setTreeParent(CompositeElement parent) { - if(next == null || isLast()){ + if (next == null || isLast()) { next = parent; setLast(true); } } public synchronized void setTreeNext(TreeElement next) { - if(next != null){ + if (next != null) { setLast(false); this.next = next; } - else{ - if(!isLast()){ + else { + if (!isLast()) { this.next = getTreeParent(); setLast(true); } @@ -135,6 +140,10 @@ public abstract class LeafElement extends TreeElement { throw new RuntimeException(new IncorrectOperationException("Leaf elements cannot have children.")); } + public void addChild(ASTNode child) { + throw new RuntimeException(new IncorrectOperationException("Leaf elements cannot have children.")); + } + public void removeChild(ASTNode child) { throw new RuntimeException(new IncorrectOperationException("Leaf elements cannot have children.")); } diff --git a/source/com/intellij/psi/impl/source/tree/LeafPsiElement.java b/source/com/intellij/psi/impl/source/tree/LeafPsiElement.java index 8d06a2d47641..ba411b959d65 100644 --- a/source/com/intellij/psi/impl/source/tree/LeafPsiElement.java +++ b/source/com/intellij/psi/impl/source/tree/LeafPsiElement.java @@ -18,7 +18,7 @@ import com.intellij.lang.ASTNode; public class LeafPsiElement extends LeafElementImpl implements PsiElement { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.LeafPsiElement"); - protected LeafPsiElement(IElementType type, char[] buffer, int startOffset, int endOffset, int lexerState, CharTable table) { + public LeafPsiElement(IElementType type, char[] buffer, int startOffset, int endOffset, int lexerState, CharTable table) { super(type, buffer, startOffset, endOffset, table); setState(lexerState); } diff --git a/source/com/intellij/psi/impl/source/tree/TreeElement.java b/source/com/intellij/psi/impl/source/tree/TreeElement.java index 37677d77055d..07758f860a14 100644 --- a/source/com/intellij/psi/impl/source/tree/TreeElement.java +++ b/source/com/intellij/psi/impl/source/tree/TreeElement.java @@ -14,11 +14,12 @@ import com.intellij.lang.ASTNode; public abstract class TreeElement extends ElementBase implements ASTNode, Constants, Cloneable { public static final TreeElement[] EMPTY_ARRAY = new TreeElement[0]; protected TreeElement next = null; // this var could be not only next element pointer in ChildElements - // use it _VERY_ carefuly!! If you are not sure use apropariate getter (getTreeNext()). + // use it _VERY_ carefuly!! If you are not sure use apropariate getter (getTreeNext()). public static final Key MANAGER_KEY = Key.create("Element.MANAGER_KEY"); + public static final Key PSI_ELEMENT_KEY = Key.create("Element.PsiElement"); - public Object clone(){ + public Object clone() { TreeElement clone = (TreeElement)super.clone(); clone.clearCaches(); clone.next = null; @@ -27,18 +28,18 @@ public abstract class TreeElement extends ElementBase implements ASTNode, Consta public ASTNode copyElement() { if (getTreeParent() != null) { - CompositeElement parentCopy = (CompositeElement)getTreeParent().copyElement(); - synchronized (PsiLock.LOCK) { - int index = 0; - for (ASTNode child = getTreeParent().firstChild; child != this; child = child.getTreeNext()) { - index++; - } - ASTNode child; - for (child = parentCopy.firstChild; index > 0; child = child.getTreeNext(), index--) { - } -//child.putUserData(MANAGER_KEY, getManager()); //? - return child; + CompositeElement parentCopy = (CompositeElement)getTreeParent().copyElement(); + synchronized (PsiLock.LOCK) { + int index = 0; + for (ASTNode child = getTreeParent().firstChild; child != this; child = child.getTreeNext()) { + index++; } + ASTNode child; + for (child = parentCopy.firstChild; index > 0; child = child.getTreeNext(), index--) { + } + //child.putUserData(MANAGER_KEY, getManager()); //? + return child; + } } else { final ASTNode clone = (ASTNode)clone(); @@ -49,17 +50,17 @@ public abstract class TreeElement extends ElementBase implements ASTNode, Consta public PsiManager getManager() { TreeElement element; - for(element = this; element.getTreeParent() != null; element = element.getTreeParent()){ + for (element = this; element.getTreeParent() != null; element = element.getTreeParent()) { } - if (element instanceof FileElement){ //TODO!! + if (element instanceof FileElement) { //TODO!! return element.getManager(); } - else{ + else { if (getTreeParent() != null) { return getTreeParent().getManager(); } - else{ + else { return getUserData(MANAGER_KEY); } } @@ -70,6 +71,7 @@ public abstract class TreeElement extends ElementBase implements ASTNode, Consta public abstract LeafElement findLeafElementAt(int offset); public abstract String getText(); + public abstract String getText(CharTable table); public abstract char[] textToCharArray(); @@ -77,35 +79,35 @@ public abstract class TreeElement extends ElementBase implements ASTNode, Consta public abstract boolean textContains(char c); public TextRange getTextRange() { - synchronized (PsiLock.LOCK) { - int start = getStartOffset(); - return new TextRange(start, start + getTextLength()); - } + synchronized (PsiLock.LOCK) { + int start = getStartOffset(); + return new TextRange(start, start + getTextLength()); + } } public int getStartOffset() { - synchronized (PsiLock.LOCK) { - final CompositeElement parent = getTreeParent(); - if (parent == null) return 0; - int offset = parent.getStartOffset(); - final CharTable table = SharedImplUtil.findCharTableByTree(this); - for (TreeElement element1 = parent.firstChild; element1 != this; element1 = element1.getTreeNext()) { - offset += element1.getTextLength(table); - } - return offset; + synchronized (PsiLock.LOCK) { + final CompositeElement parent = getTreeParent(); + if (parent == null) return 0; + int offset = parent.getStartOffset(); + final CharTable table = SharedImplUtil.findCharTableByTree(this); + for (TreeElement element1 = parent.firstChild; element1 != this; element1 = element1.getTreeNext()) { + offset += element1.getTextLength(table); } + return offset; + } } - public final int getStartOffsetInParent(){ + public final int getStartOffsetInParent() { if (getTreeParent() == null) return -1; int offset = 0; - for(ASTNode child = getTreeParent().getFirstChildNode(); child != this; child = child.getTreeNext()){ + for (ASTNode child = getTreeParent().getFirstChildNode(); child != this; child = child.getTreeNext()) { offset += child.getTextLength(); } return offset; } - public int getTextOffset(){ + public int getTextOffset() { return getStartOffset(); } @@ -113,25 +115,25 @@ public abstract class TreeElement extends ElementBase implements ASTNode, Consta return textMatches(this, buffer, startOffset, endOffset) == endOffset; } - public int textStartsWith(CharSequence buffer, int startOffset, int endOffset){ + public int textStartsWith(CharSequence buffer, int startOffset, int endOffset) { return textMatches(this, buffer, startOffset, endOffset); } private static int textMatches(ASTNode element, CharSequence buffer, int startOffset, int endOffset) { - synchronized (PsiLock.LOCK) { - if (element instanceof LeafElement) { - final LeafElement leaf = (LeafElement)element; - return leaf.textMatches(buffer, startOffset); - } - else { - int curOffset = startOffset; - for (ASTNode child = element.getFirstChildNode(); child != null; child = child.getTreeNext()) { - curOffset = textMatches(child, buffer, curOffset, endOffset); - if (curOffset == -1) return -1; - } - return curOffset; - } + synchronized (PsiLock.LOCK) { + if (element instanceof LeafElement) { + final LeafElement leaf = (LeafElement)element; + return leaf.textMatches(buffer, startOffset); } + else { + int curOffset = startOffset; + for (ASTNode child = element.getFirstChildNode(); child != null; child = child.getTreeNext()) { + curOffset = textMatches(child, buffer, curOffset, endOffset); + if (curOffset == -1) return -1; + } + return curOffset; + } + } } public boolean textMatches(CharSequence seq) { @@ -143,7 +145,7 @@ public abstract class TreeElement extends ElementBase implements ASTNode, Consta return textMatches(element.getText()); } - public String toString(){ + public String toString() { return "Element" + "(" + getElementType().toString() + ")"; } @@ -159,7 +161,7 @@ public abstract class TreeElement extends ElementBase implements ASTNode, Consta public abstract void setTreePrev(TreeElement prev); - public void setTreeNext(TreeElement next){ + public void setTreeNext(TreeElement next) { this.next = next; } @@ -167,13 +169,13 @@ public abstract class TreeElement extends ElementBase implements ASTNode, Consta public abstract int getTextLength(CharTable charTableByTree); - public void clearCaches(){} + public void clearCaches() { } - public final boolean equals(Object obj){ + public final boolean equals(Object obj) { return super.equals(obj); } - public final int hashCode(){ + public final int hashCode() { return super.hashCode(); }