Draft PsiBuilder implementation.

This commit is contained in:
Maxim Shafirov
2005-01-22 01:10:55 +03:00
parent 657500aeb8
commit df752b5d8a
11 changed files with 683 additions and 148 deletions
@@ -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> T getCopyableUserData(Key<T> key);
<T> void putCopyableUserData(Key<T> key, T value);
}
@@ -13,6 +13,8 @@ import com.intellij.psi.tree.IElementType;
*/
public interface ParserDefinition {
Lexer createLexer();
PsiParser createParser();
PsiElement createElement(ASTNode node);
}
+19 -2
View File
@@ -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();
}
+2 -1
View File
@@ -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);
}
@@ -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<Lexem> myLexems = new ArrayList<Lexem>();
private List<Object> myProduction = new ArrayList<Object>();
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;
}
}
@@ -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> T getCopyableUserData(Key<T> key) {
return myNode.getCopyableUserData(key);
}
public <T> void putCopyableUserData(Key<T> key, T value) {
myNode.putCopyableUserData(key, value);
}
public ASTNode getNode() {
return myNode;
}
}
@@ -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;
@@ -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);
}
@@ -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."));
}
@@ -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);
}
@@ -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<PsiManager> MANAGER_KEY = Key.create("Element.MANAGER_KEY");
public static final Key<PsiElement> 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();
}