/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.plugin.ij.lang.psi.stubs.elements;
import com.google.common.base.Objects;
import com.intellij.ide.DataManager;
import com.intellij.lang.ASTNode;
import com.intellij.lang.Language;
import com.intellij.openapi.actionSystem.DataProvider;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.impl.text.PsiAwareTextEditorImpl;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PlainTextTokenTypes;
import com.intellij.psi.StubBuilder;
import com.intellij.psi.impl.TextBlock;
import com.intellij.psi.impl.source.tree.LeafElement;
import com.intellij.psi.impl.source.tree.PlainTextASTFactory;
import com.intellij.psi.stubs.StubElement;
import com.intellij.psi.stubs.StubInputStream;
import com.intellij.psi.stubs.StubOutputStream;
import com.intellij.psi.tree.IStubFileElementType;
import com.intellij.util.io.StringRef;
import gw.config.CommonServices;
import gw.lang.reflect.TypeSystem;
import gw.lang.reflect.module.IModule;
import gw.plugin.ij.lang.psi.impl.AbstractGosuClassFileImpl;
import gw.plugin.ij.lang.psi.stubs.GosuFileStub;
import gw.plugin.ij.lang.psi.stubs.GosuFileStubBuilder;
import gw.plugin.ij.util.ExceptionUtil;
import gw.plugin.ij.util.FileUtil;
import gw.plugin.ij.util.GosuModuleUtil;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
import java.io.IOException;
public class GosuStubFileElementType extends IStubFileElementType<GosuFileStub> {
public static final Key<Boolean> KEY_RAW_TEXT = new Key<>( "RawText" );
private final String _externalId;
private boolean _bSkipParsingWhileTyping;
public GosuStubFileElementType(String externalId, Language language) {
super(externalId, language);
_externalId = externalId;
_bSkipParsingWhileTyping = true;
}
@NotNull
public StubBuilder getBuilder() {
return new GosuFileStubBuilder();
}
@Override
public int getStubVersion() {
return super.getStubVersion() + 2;
}
public String getExternalId() {
return _externalId;
}
public boolean isSkipParsingWhileTyping() {
return _bSkipParsingWhileTyping;
}
public void setSkipParsingWhileTyping( boolean bParseFastWhileTyping ) {
_bSkipParsingWhileTyping = bParseFastWhileTyping;
}
public ASTNode parseContents(@NotNull ASTNode chameleon) {
AbstractGosuClassFileImpl psiFile = (AbstractGosuClassFileImpl) chameleon.getPsi();
IModule module = psiFile.getModule();
TypeSystem.pushModule(module);
try {
//TODO-dp remove TypeSystem.getCurrentCompilingType() != null which was put in to avoid reentrancy of the Gosu parser
String content = psiFile.getText();
if (CommonServices.getPlatformHelper().isPathIgnored(FileUtil.getFileFromPsi(psiFile).getPath()) ||
isDoNotVerify( content ) ||
TypeSystem.getCurrentCompilingType() != null) {
return new PlainTextASTFactory().createLeaf(PlainTextTokenTypes.PLAIN_TEXT, psiFile.getText());
}
else if( isSkipParsingWhileTyping() && wasModifiedInEditor( psiFile, content ) ) {
//System.out.println( "Skipping Psi: " + psiFile.getName() );
return skipParsing( psiFile );
}
else {
//System.out.println( "Building Psi: " + psiFile.getName() );
return psiFile.parse(chameleon); // bad: refresh(psiFile);
}
} catch (RuntimeException e) {
if (ExceptionUtil.isWrappedCanceled(e)) {
throw new ProcessCanceledException(e.getCause());
}
printStackTrace(e, psiFile);
} catch (Throwable e) {
printStackTrace(e, psiFile);
} finally {
TypeSystem.popModule(module);
}
return null;
}
private ASTNode skipParsing( AbstractGosuClassFileImpl psiFile ) {
LeafElement node = new PlainTextASTFactory().createLeaf( PlainTextTokenTypes.PLAIN_TEXT, psiFile.getText() );
node.putUserData( KEY_RAW_TEXT, true );
return node;
}
private boolean wasModifiedInEditor( AbstractGosuClassFileImpl psiFile, String content ) {
AbstractGosuClassFileImpl originalFile = (AbstractGosuClassFileImpl)psiFile.getOriginalFile();
if( originalFile != null ) {
if( !originalFile.isReparse() ) {
final Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
FileEditor editor = PlatformDataKeys.FILE_EDITOR.getData( DataManager.getInstance().getDataContext(owner));
if( editor instanceof PsiAwareTextEditorImpl && !isSingleLineEditor( (PsiAwareTextEditorImpl)editor ) ) {
DataProvider component = ((PsiAwareTextEditorImpl)editor).getComponent();
if( component != null ) {
VirtualFile vfile = (VirtualFile)component.getData( PlatformDataKeys.VIRTUAL_FILE.getName() );
if( vfile != null && vfile.equals( originalFile.getVirtualFile() ) ) {
TextBlock textBlock = TextBlock.get( originalFile );
boolean bChangedText = textBlock != null && !textBlock.isEmpty();
if( !changeHasChar( new char[]{'.', '\n'}, content, textBlock ) ) {
if( bChangedText ) {
//System.out.println( psiFile.getName() + " Changed" );
}
return bChangedText;
}
}
}
}
}
else {
//originalFile.setReparse( false );
}
}
return false;
}
private boolean isSingleLineEditor( PsiAwareTextEditorImpl editor ) {
Editor activeEditor = editor.getEditor();
return activeEditor != null && activeEditor.isOneLineMode();
}
private boolean changeHasChar( char[] chars, String content, TextBlock tb ) {
int iStart = tb.getStartOffset();
int iEnd = tb.getTextEndOffset();
if( iStart >= 0 && iStart < iEnd ) {
if( content.length() > iEnd ) {
for( char c : chars ) {
int iChar = content.indexOf( c, iStart );
if( iChar > 0 && iChar < iEnd ) {
return true;
}
}
}
}
return false;
}
// private boolean isFromTyping( AbstractGosuClassFileImpl psiFile ) {
// AbstractGosuClassFileImpl originalFile = (AbstractGosuClassFileImpl)psiFile.getOriginalFile();
// if( originalFile != null ) {
// if( !originalFile.isReparse() ) {
// //!! this blows up b/c we may not be in the dispatch thread (can't invokeAndWait() either b/c deadlock)
// FileEditorManager fem = FileEditorManagerImpl.getInstance( psiFile.getProject() );
// VirtualFile vfile = originalFile.getVirtualFile();
// if( vfile != null && fem.getSelectedEditor( vfile ) != null ) {
// TextBlock textBlock = TextBlock.get( originalFile );
// boolean bChangedText = textBlock != null && !textBlock.isEmpty();
// if( bChangedText ) {
// System.out.println( psiFile.getName() + " Changed" );
// }
// return bChangedText;
// }
// }
// else {
// originalFile.setReparse( false );
// }
// }
// return false;
// }
private boolean isDoNotVerify(String text) {
return false; //text != null && (text.contains("@DoNotVerifyResource") || text.contains("@gw.testharness.DoNotVerifyResource"));
}
private void printStackTrace(@NotNull Throwable e, @NotNull AbstractGosuClassFileImpl file) {
final String message = Objects.firstNonNull(e.getMessage(), e.getClass().getSimpleName());
new Exception(String.format("Error parsing %s: %s", file.getQualifiedClassNameFromFile(), message), e).printStackTrace();
}
@Override
public void serialize(@NotNull final GosuFileStub stub, @NotNull final StubOutputStream dataStream) throws IOException {
IModule rootModule = stub.getPsi() != null
? GosuModuleUtil.getGlobalModule(stub.getProject())
: null;
if( rootModule != null ) {
TypeSystem.pushModule( rootModule );
}
try {
dataStream.writeName( stub.getPackageName().toString() );
dataStream.writeName(stub.getName().toString());
dataStream.writeName(stub.getExt().toString());
}
finally {
if( rootModule != null ) {
TypeSystem.popModule( rootModule );
}
}
}
@NotNull
@Override
public GosuFileStub deserialize(@NotNull final StubInputStream dataStream, final StubElement parentStub) throws IOException {
final StringRef packageName = dataStream.readName();
final StringRef name = dataStream.readName();
final StringRef ext = dataStream.readName();
return new GosuFileStub(packageName, name, ext);
}
}