Don't block EDT while counting information for Resolve and Bytecode window
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2010-2012 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.jet.plugin.internal;
|
||||
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.psi.JetFile;
|
||||
|
||||
/**
|
||||
* @author Nikolay Krasko
|
||||
*/
|
||||
public class Location {
|
||||
@Nullable
|
||||
final Editor editor;
|
||||
|
||||
@Nullable
|
||||
final JetFile jetFile;
|
||||
|
||||
final long modificationStamp;
|
||||
|
||||
final int startOffset;
|
||||
final int endOffset;
|
||||
|
||||
private Location(@Nullable Editor editor, Project project) {
|
||||
this.editor = editor;
|
||||
|
||||
if (editor != null) {
|
||||
modificationStamp = editor.getDocument().getModificationStamp();
|
||||
startOffset = editor.getSelectionModel().getSelectionStart();
|
||||
endOffset = editor.getSelectionModel().getSelectionEnd();
|
||||
|
||||
VirtualFile vFile = ((EditorEx) editor).getVirtualFile();
|
||||
if (vFile == null) {
|
||||
jetFile = null;
|
||||
}
|
||||
else {
|
||||
PsiFile psiFile = PsiManager.getInstance(project).findFile(vFile);
|
||||
jetFile = psiFile instanceof JetFile ? (JetFile) psiFile : null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
modificationStamp = 0;
|
||||
startOffset = 0;
|
||||
endOffset = 0;
|
||||
jetFile = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Location fromEditor(Editor editor, Project project) {
|
||||
return new Location(editor, project);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JetFile getJetFile() {
|
||||
return jetFile;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Editor getEditor() {
|
||||
return editor;
|
||||
}
|
||||
|
||||
public int getStartOffset() {
|
||||
return startOffset;
|
||||
}
|
||||
|
||||
public int getEndOffset() {
|
||||
return endOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof Location)) return false;
|
||||
|
||||
Location location = (Location)o;
|
||||
|
||||
if (modificationStamp != location.modificationStamp) return false;
|
||||
if (endOffset != location.endOffset) return false;
|
||||
if (startOffset != location.startOffset) return false;
|
||||
if (editor != null ? !editor.equals(location.editor) : location.editor != null) return false;
|
||||
if (jetFile != null ? !jetFile.equals(location.jetFile) : location.jetFile != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = editor != null ? editor.hashCode() : 0;
|
||||
result = 31 * result + (jetFile != null ? jetFile.hashCode() : 0);
|
||||
result = 31 * result + (int)(modificationStamp ^ (modificationStamp >>> 32));
|
||||
result = 31 * result + startOffset;
|
||||
result = 31 * result + endOffset;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -27,22 +27,21 @@ import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.editor.ScrollType;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.util.Alarm;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.analyzer.AnalyzeExhaust;
|
||||
import org.jetbrains.jet.codegen.ClassBuilderFactories;
|
||||
import org.jetbrains.jet.codegen.ClassFileFactory;
|
||||
import org.jetbrains.jet.codegen.CompilationErrorHandler;
|
||||
import org.jetbrains.jet.codegen.GenerationState;
|
||||
import org.jetbrains.jet.lang.psi.JetFile;
|
||||
import org.jetbrains.jet.plugin.internal.Location;
|
||||
import org.jetbrains.jet.plugin.project.WholeProjectAnalyzerFacade;
|
||||
import org.jetbrains.jet.plugin.util.LongRunningReadTask;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
@@ -54,67 +53,89 @@ import java.util.List;
|
||||
import java.util.Scanner;
|
||||
|
||||
public class BytecodeToolwindow extends JPanel implements Disposable {
|
||||
private static final int UPDATE_DELAY = 500;
|
||||
private static final int UPDATE_DELAY = 1000;
|
||||
private static final String DEFAULT_TEXT = "/*\n" +
|
||||
"Generated bytecode for Kotlin source file.\n" +
|
||||
"No Kotlin source file is opened.\n" +
|
||||
"*/";
|
||||
|
||||
private final Editor myEditor;
|
||||
private final Alarm myUpdateAlarm;
|
||||
private Location myCurrentLocation;
|
||||
private final Project myProject;
|
||||
|
||||
|
||||
public BytecodeToolwindow(Project project) {
|
||||
super(new BorderLayout());
|
||||
myProject = project;
|
||||
myEditor =
|
||||
EditorFactory.getInstance().createEditor(EditorFactory.getInstance().createDocument(""), project, JavaFileType.INSTANCE, true);
|
||||
add(myEditor.getComponent());
|
||||
myUpdateAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD, this);
|
||||
myUpdateAlarm.addRequest(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
myUpdateAlarm.addRequest(this, UPDATE_DELAY);
|
||||
Location location = Location.fromEditor(FileEditorManager.getInstance(myProject).getSelectedTextEditor());
|
||||
if (!Comparing.equal(location, myCurrentLocation)) {
|
||||
updateBytecode(location, myCurrentLocation);
|
||||
myCurrentLocation = location;
|
||||
}
|
||||
public class UpdateBytecodeToolWindowTask extends LongRunningReadTask<Location, String> {
|
||||
@Override
|
||||
protected Location prepareRequestInfo() {
|
||||
Location location = Location.fromEditor(FileEditorManager.getInstance(myProject).getSelectedTextEditor(), myProject);
|
||||
if (location.getEditor() == null || location.getJetFile() == null) {
|
||||
return null;
|
||||
}
|
||||
}, UPDATE_DELAY);
|
||||
}
|
||||
|
||||
private void updateBytecode(Location location, Location oldLocation) {
|
||||
Editor editor = location.editor;
|
||||
return location;
|
||||
}
|
||||
|
||||
if (editor == null) {
|
||||
@NotNull
|
||||
@Override
|
||||
protected Location cloneRequestInfo(@NotNull Location location) {
|
||||
Location newLocation = super.cloneRequestInfo(location);
|
||||
assert location.equals(newLocation) : "cloneRequestInfo should generate same location object";
|
||||
return newLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void hideResultOnInvalidLocation() {
|
||||
setText(DEFAULT_TEXT);
|
||||
}
|
||||
else {
|
||||
VirtualFile vFile = ((EditorEx)editor).getVirtualFile();
|
||||
if (vFile == null) {
|
||||
setText(DEFAULT_TEXT);
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected String processRequest(@NotNull Location location) {
|
||||
JetFile jetFile = location.getJetFile();
|
||||
assert jetFile != null;
|
||||
|
||||
GenerationState state;
|
||||
try {
|
||||
AnalyzeExhaust binding = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile(jetFile);
|
||||
if (binding.isError()) {
|
||||
return printStackTraceToString(binding.getError());
|
||||
}
|
||||
state = new GenerationState(jetFile.getProject(), ClassBuilderFactories.TEXT, binding, Collections.singletonList(jetFile));
|
||||
state.compileCorrectFiles(CompilationErrorHandler.THROW_EXCEPTION);
|
||||
}
|
||||
catch (ProcessCanceledException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e) {
|
||||
return printStackTraceToString(e);
|
||||
}
|
||||
|
||||
StringBuilder answer = new StringBuilder();
|
||||
|
||||
final ClassFileFactory factory = state.getFactory();
|
||||
for (String filename : factory.files()) {
|
||||
answer.append("// ================ ");
|
||||
answer.append(filename);
|
||||
answer.append(" =================\n");
|
||||
answer.append(factory.asText(filename)).append("\n\n");
|
||||
}
|
||||
|
||||
return answer.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResultReady(@NotNull Location requestInfo, final String resultText) {
|
||||
Editor editor = requestInfo.getEditor();
|
||||
assert editor != null;
|
||||
|
||||
if (resultText == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
PsiFile psiFile = PsiManager.getInstance(myProject).findFile(vFile);
|
||||
if (!(psiFile instanceof JetFile)) {
|
||||
setText(DEFAULT_TEXT);
|
||||
return;
|
||||
}
|
||||
setText(resultText);
|
||||
|
||||
if (oldLocation == null ||
|
||||
!Comparing.equal(oldLocation.editor, location.editor) ||
|
||||
oldLocation.modificationStamp != location.modificationStamp) {
|
||||
setText(generateToText((JetFile)psiFile));
|
||||
}
|
||||
int fileStartOffset = requestInfo.getStartOffset();
|
||||
int fileEndOffset = requestInfo.getEndOffset();
|
||||
|
||||
Document document = editor.getDocument();
|
||||
int startLine = document.getLineNumber(location.startOffset);
|
||||
int endLine = document.getLineNumber(location.endOffset);
|
||||
if (endLine > startLine && location.endOffset > 0 && document.getCharsSequence().charAt(location.endOffset - 1) == '\n') {
|
||||
int startLine = document.getLineNumber(fileStartOffset);
|
||||
int endLine = document.getLineNumber(fileEndOffset);
|
||||
if (endLine > startLine && fileEndOffset > 0 && document.getCharsSequence().charAt(fileEndOffset - 1) == '\n') {
|
||||
endLine--;
|
||||
}
|
||||
|
||||
@@ -135,6 +156,37 @@ public class BytecodeToolwindow extends JPanel implements Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
private final Editor myEditor;
|
||||
private final Alarm myUpdateAlarm;
|
||||
private final Project myProject;
|
||||
|
||||
private UpdateBytecodeToolWindowTask currentTask = null;
|
||||
|
||||
public BytecodeToolwindow(Project project) {
|
||||
super(new BorderLayout());
|
||||
myProject = project;
|
||||
myEditor = EditorFactory.getInstance().createEditor(
|
||||
EditorFactory.getInstance().createDocument(""), project, JavaFileType.INSTANCE, true);
|
||||
add(myEditor.getComponent());
|
||||
|
||||
myUpdateAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD, this);
|
||||
myUpdateAlarm.addRequest(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
myUpdateAlarm.addRequest(this, UPDATE_DELAY);
|
||||
UpdateBytecodeToolWindowTask task = new UpdateBytecodeToolWindowTask();
|
||||
task.init();
|
||||
|
||||
if (task.shouldStart(currentTask)) {
|
||||
currentTask = task;
|
||||
currentTask.run();
|
||||
}
|
||||
}
|
||||
}, UPDATE_DELAY);
|
||||
|
||||
setText(DEFAULT_TEXT);
|
||||
}
|
||||
|
||||
private static Pair<Integer, Integer> mapLines(String text, int startLine, int endLine) {
|
||||
int byteCodeLine = 0;
|
||||
int byteCodeStartLine = -1;
|
||||
@@ -192,109 +244,29 @@ public class BytecodeToolwindow extends JPanel implements Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
private void setText(final String text) {
|
||||
private static String printStackTraceToString(Throwable e) {
|
||||
StringWriter out = new StringWriter(1024);
|
||||
PrintWriter printWriter = new PrintWriter(out);
|
||||
try {
|
||||
e.printStackTrace(printWriter);
|
||||
return out.toString().replace("\r", "");
|
||||
}
|
||||
finally {
|
||||
printWriter.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void setText(@NotNull final String resultText) {
|
||||
new WriteCommandAction(myProject) {
|
||||
@Override
|
||||
protected void run(Result result) throws Throwable {
|
||||
myEditor.getDocument().setText(text);
|
||||
myEditor.getDocument().setText(resultText);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
protected String generateToText(JetFile file) {
|
||||
GenerationState state;
|
||||
try {
|
||||
AnalyzeExhaust binding = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile(file);
|
||||
// AnalyzingUtils.throwExceptionOnErrors(binding);
|
||||
if (binding.isError()) {
|
||||
return printStackTraceToString(binding.getError());
|
||||
}
|
||||
state = new GenerationState(file.getProject(), ClassBuilderFactories.TEXT, binding, Collections.singletonList(file));
|
||||
state.compileCorrectFiles(CompilationErrorHandler.THROW_EXCEPTION);
|
||||
}
|
||||
catch (Exception e) {
|
||||
return printStackTraceToString(e);
|
||||
}
|
||||
|
||||
|
||||
StringBuilder answer = new StringBuilder();
|
||||
|
||||
final ClassFileFactory factory = state.getFactory();
|
||||
for (String filename : factory.files()) {
|
||||
answer.append("// ================ ");
|
||||
answer.append(filename);
|
||||
answer.append(" =================\n");
|
||||
answer.append(factory.asText(filename)).append("\n\n");
|
||||
}
|
||||
|
||||
return answer.toString();
|
||||
}
|
||||
|
||||
private static String printStackTraceToString(Throwable e) {StringWriter out = new StringWriter(1024);
|
||||
e.printStackTrace(new PrintWriter(out));
|
||||
return out.toString().replace("\r", "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
EditorFactory.getInstance().releaseEditor(myEditor);
|
||||
}
|
||||
|
||||
public static class Location {
|
||||
final Editor editor;
|
||||
final long modificationStamp;
|
||||
final int startOffset;
|
||||
final int endOffset;
|
||||
|
||||
private Location(Editor editor) {
|
||||
this.editor = editor;
|
||||
modificationStamp = editor != null ? editor.getDocument().getModificationStamp() : 0;
|
||||
startOffset = editor != null ? editor.getSelectionModel().getSelectionStart() : 0;
|
||||
endOffset = editor != null ? editor.getSelectionModel().getSelectionEnd() : 0;
|
||||
}
|
||||
|
||||
public static Location fromEditor(Editor editor) {
|
||||
return new Location(editor);
|
||||
}
|
||||
|
||||
public Editor getEditor() {
|
||||
return editor;
|
||||
}
|
||||
|
||||
public long getModificationStamp() {
|
||||
return modificationStamp;
|
||||
}
|
||||
|
||||
public int getStartOffset() {
|
||||
return startOffset;
|
||||
}
|
||||
|
||||
public int getEndOffset() {
|
||||
return endOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof Location)) return false;
|
||||
|
||||
Location location = (Location)o;
|
||||
|
||||
if (endOffset != location.endOffset) return false;
|
||||
if (modificationStamp != location.modificationStamp) return false;
|
||||
if (startOffset != location.startOffset) return false;
|
||||
if (editor != null ? !editor.equals(location.editor) : location.editor != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = editor != null ? editor.hashCode() : 0;
|
||||
result = 31 * result + (int)(modificationStamp ^ (modificationStamp >>> 32));
|
||||
result = 31 * result + startOffset;
|
||||
result = 31 * result + endOffset;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,17 +24,14 @@ import com.intellij.openapi.application.Result;
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
import com.intellij.util.Alarm;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.descriptors.*;
|
||||
import org.jetbrains.jet.lang.psi.JetElement;
|
||||
@@ -47,10 +44,10 @@ import org.jetbrains.jet.lang.resolve.calls.inference.BoundsOwner;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.jetbrains.jet.plugin.JetFileType;
|
||||
import org.jetbrains.jet.plugin.internal.codewindow.BytecodeToolwindow;
|
||||
import org.jetbrains.jet.plugin.internal.Location;
|
||||
import org.jetbrains.jet.plugin.project.WholeProjectAnalyzerFacade;
|
||||
import org.jetbrains.jet.plugin.util.LongRunningReadTask;
|
||||
import org.jetbrains.jet.util.slicedmap.ReadOnlySlice;
|
||||
import org.jetbrains.jet.util.slicedmap.WritableSlice;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
@@ -67,110 +64,121 @@ public class ResolveToolwindow extends JPanel implements Disposable {
|
||||
|
||||
public static final String BAR = "\n\n===\n\n";
|
||||
|
||||
private static final int UPDATE_DELAY = 500;
|
||||
private static final int UPDATE_DELAY = 1000;
|
||||
private static final String DEFAULT_TEXT = "/*\n" +
|
||||
"Information about symbols resolved by\nKotlin compiler.\n" +
|
||||
"No Kotlin source file is opened.\n" +
|
||||
"*/";
|
||||
private final Editor myEditor;
|
||||
private final Alarm myUpdateAlarm;
|
||||
private BytecodeToolwindow.Location myCurrentLocation;
|
||||
private final Project myProject;
|
||||
|
||||
private static final String NO_REFERENCE_TEXT = "/*\n" +
|
||||
"Information about symbols resolved by\nKotlin compiler.\n" +
|
||||
"Invalid place for getting reference information.\n" +
|
||||
"*/";
|
||||
|
||||
private class UpdateResolveToolWindowTask extends LongRunningReadTask<Location, String> {
|
||||
@Override
|
||||
protected Location prepareRequestInfo() {
|
||||
Location location = Location.fromEditor(FileEditorManager.getInstance(myProject).getSelectedTextEditor(), myProject);
|
||||
if (location.getEditor() == null || location.getJetFile() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void hideResultOnInvalidLocation() {
|
||||
setText(DEFAULT_TEXT);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected String processRequest(@NotNull Location requestInfo) {
|
||||
JetFile jetFile = requestInfo.getJetFile();
|
||||
assert jetFile != null;
|
||||
|
||||
int startOffset = requestInfo.getStartOffset();
|
||||
int endOffset = requestInfo.getEndOffset();
|
||||
|
||||
BindingContext bindingContext = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile(jetFile).getBindingContext();
|
||||
|
||||
PsiElement elementAtOffset;
|
||||
if (startOffset == endOffset) {
|
||||
elementAtOffset = PsiUtilCore.getElementAtOffset(jetFile, startOffset);
|
||||
}
|
||||
else {
|
||||
PsiElement start = PsiUtilCore.getElementAtOffset(jetFile, startOffset);
|
||||
PsiElement end = PsiUtilCore.getElementAtOffset(jetFile, endOffset - 1);
|
||||
elementAtOffset = PsiTreeUtil.findCommonParent(start, end);
|
||||
}
|
||||
|
||||
PsiElement elementWithDebugInfo = findData(bindingContext, elementAtOffset, RESOLUTION_DEBUG_INFO);
|
||||
if (elementWithDebugInfo != null) {
|
||||
return renderDebugInfo(elementWithDebugInfo, bindingContext.get(RESOLUTION_DEBUG_INFO, elementWithDebugInfo), null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
PsiElement elementWithResolvedCall = findData(bindingContext, elementAtOffset, (ReadOnlySlice) RESOLVED_CALL);
|
||||
if (elementWithResolvedCall instanceof JetElement) {
|
||||
return renderDebugInfo(elementWithResolvedCall, null,
|
||||
bindingContext.get(RESOLVED_CALL, (JetElement) elementWithResolvedCall));
|
||||
}
|
||||
|
||||
JetExpression parentExpression = (elementAtOffset instanceof JetExpression) ?
|
||||
(JetExpression) elementAtOffset :
|
||||
PsiTreeUtil.getParentOfType(elementAtOffset, JetExpression.class);
|
||||
|
||||
if (parentExpression != null) {
|
||||
JetType type = bindingContext.get(EXPRESSION_TYPE, parentExpression);
|
||||
String text = parentExpression + "|" + parentExpression.getText() + "| : " + type;
|
||||
if (parentExpression instanceof JetReferenceExpression) {
|
||||
JetReferenceExpression referenceExpression = (JetReferenceExpression) parentExpression;
|
||||
DeclarationDescriptor target = bindingContext.get(REFERENCE_TARGET, referenceExpression);
|
||||
text += "\nReference target: \n" + target;
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
return NO_REFERENCE_TEXT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResultReady(@NotNull Location requestInfo, @Nullable final String resultText) {
|
||||
if (resultText != null) {
|
||||
setText(resultText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final Alarm myUpdateAlarm;
|
||||
private final Editor myEditor;
|
||||
private UpdateResolveToolWindowTask currentTask = null;
|
||||
|
||||
private final Project myProject;
|
||||
|
||||
public ResolveToolwindow(Project project) {
|
||||
super(new BorderLayout());
|
||||
myProject = project;
|
||||
myEditor = EditorFactory.getInstance().createEditor(EditorFactory.getInstance().createDocument(""), project, JetFileType.INSTANCE, true);
|
||||
myEditor = EditorFactory.getInstance()
|
||||
.createEditor(EditorFactory.getInstance().createDocument(""), project, JetFileType.INSTANCE, true);
|
||||
add(myEditor.getComponent());
|
||||
myUpdateAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD, this);
|
||||
myUpdateAlarm.addRequest(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
myUpdateAlarm.addRequest(this, UPDATE_DELAY);
|
||||
BytecodeToolwindow.Location location = BytecodeToolwindow.Location.fromEditor(FileEditorManager.getInstance(myProject).getSelectedTextEditor());
|
||||
if (!Comparing.equal(location, myCurrentLocation)) {
|
||||
render(location, myCurrentLocation);
|
||||
myCurrentLocation = location;
|
||||
UpdateResolveToolWindowTask task = new UpdateResolveToolWindowTask();
|
||||
task.init();
|
||||
|
||||
if (task.shouldStart(currentTask)) {
|
||||
currentTask = task;
|
||||
currentTask.run();
|
||||
}
|
||||
}
|
||||
}, UPDATE_DELAY);
|
||||
}
|
||||
|
||||
private void render(BytecodeToolwindow.Location location, BytecodeToolwindow.Location oldLocation) {
|
||||
Editor editor = location.getEditor();
|
||||
if (editor == null) {
|
||||
setText(DEFAULT_TEXT);
|
||||
}
|
||||
else {
|
||||
VirtualFile vFile = ((EditorEx) editor).getVirtualFile();
|
||||
if (vFile == null) {
|
||||
setText(DEFAULT_TEXT);
|
||||
return;
|
||||
}
|
||||
|
||||
PsiFile psiFile = PsiManager.getInstance(myProject).findFile(vFile);
|
||||
if (!(psiFile instanceof JetFile)) {
|
||||
setText(DEFAULT_TEXT);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
int startOffset = location.getStartOffset();
|
||||
int endOffset = location.getEndOffset();
|
||||
if (oldLocation == null || !Comparing.equal(oldLocation.getEditor(), location.getEditor())
|
||||
|| oldLocation.getStartOffset() != startOffset
|
||||
|| oldLocation.getEndOffset() != endOffset) {
|
||||
|
||||
BindingContext bindingContext = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile((JetFile) psiFile)
|
||||
.getBindingContext();
|
||||
|
||||
|
||||
PsiElement elementAtOffset;
|
||||
if (startOffset == endOffset) {
|
||||
elementAtOffset = PsiUtilCore.getElementAtOffset(psiFile, startOffset);
|
||||
}
|
||||
else {
|
||||
PsiElement start = PsiUtilCore.getElementAtOffset(psiFile, startOffset);
|
||||
PsiElement end = PsiUtilCore.getElementAtOffset(psiFile, endOffset - 1);
|
||||
elementAtOffset = PsiTreeUtil.findCommonParent(start, end);
|
||||
}
|
||||
|
||||
PsiElement currentElement = elementAtOffset;
|
||||
|
||||
boolean callFound = false;
|
||||
|
||||
PsiElement elementWithDebugInfo = findData(bindingContext, currentElement, RESOLUTION_DEBUG_INFO);
|
||||
if (elementWithDebugInfo != null) {
|
||||
callFound = true;
|
||||
setText(renderDebugInfo(elementWithDebugInfo, bindingContext.get(RESOLUTION_DEBUG_INFO, elementWithDebugInfo), null));
|
||||
}
|
||||
else {
|
||||
PsiElement elementWithResolvedCall = findData(bindingContext, currentElement, (WritableSlice) RESOLVED_CALL);
|
||||
if (elementWithResolvedCall instanceof JetElement) {
|
||||
callFound = true;
|
||||
setText(renderDebugInfo(elementWithResolvedCall, null, bindingContext.get(RESOLVED_CALL, (JetElement) elementWithResolvedCall)));
|
||||
}
|
||||
}
|
||||
|
||||
if (!callFound) {
|
||||
|
||||
JetExpression parentExpression = (elementAtOffset instanceof JetExpression) ?
|
||||
(JetExpression) elementAtOffset :
|
||||
PsiTreeUtil.getParentOfType(elementAtOffset, JetExpression.class);
|
||||
if (parentExpression != null) {
|
||||
JetType type = bindingContext.get(EXPRESSION_TYPE, parentExpression);
|
||||
String text = parentExpression + "|" + parentExpression.getText() + "| : " + type;
|
||||
if (parentExpression instanceof JetReferenceExpression) {
|
||||
JetReferenceExpression referenceExpression = (JetReferenceExpression) parentExpression;
|
||||
DeclarationDescriptor target = bindingContext.get(REFERENCE_TARGET, referenceExpression);
|
||||
text += "\nReference target: \n" + target;
|
||||
}
|
||||
setText(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
setText(DEFAULT_TEXT);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -182,18 +190,22 @@ public class ResolveToolwindow extends JPanel implements Disposable {
|
||||
if (data != null && data != NO_DEBUG_INFO) {
|
||||
return currentElement;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
currentElement = currentElement.getParent();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String renderDebugInfo(PsiElement currentElement,
|
||||
@NotNull
|
||||
private static String renderDebugInfo(
|
||||
PsiElement currentElement,
|
||||
@Nullable ResolutionDebugInfo.Data debugInfo,
|
||||
@Nullable ResolvedCall<? extends CallableDescriptor> call) {
|
||||
@Nullable ResolvedCall<? extends CallableDescriptor> call
|
||||
) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
|
||||
if (debugInfo != null) {
|
||||
List<? extends ResolutionTask<? extends CallableDescriptor, ?>> resolutionTasks = debugInfo.get(TASKS);
|
||||
for (ResolutionTask<? extends CallableDescriptor, ?> resolutionTask : resolutionTasks) {
|
||||
@@ -215,9 +227,11 @@ public class ResolveToolwindow extends JPanel implements Disposable {
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private static void renderResolutionLogForCall(Data debugInfo,
|
||||
private static void renderResolutionLogForCall(
|
||||
Data debugInfo,
|
||||
ResolvedCallWithTrace<? extends CallableDescriptor> resolvedCall,
|
||||
StringBuilder result) {
|
||||
StringBuilder result
|
||||
) {
|
||||
result.append("Trying to call ").append(resolvedCall.getCandidateDescriptor()).append("\n");
|
||||
StringBuilder errors = debugInfo.getByKey(ERRORS, resolvedCall);
|
||||
if (errors != null) {
|
||||
@@ -254,7 +268,6 @@ public class ResolveToolwindow extends JPanel implements Disposable {
|
||||
}
|
||||
|
||||
private static String renderCall(StringBuilder builder, ResolvedCall<? extends CallableDescriptor> resolvedCall) {
|
||||
|
||||
CallableDescriptor resultingDescriptor = resolvedCall.getResultingDescriptor();
|
||||
ReceiverDescriptor receiverArgument = resolvedCall.getReceiverArgument();
|
||||
ReceiverDescriptor thisObject = resolvedCall.getThisObject();
|
||||
@@ -329,17 +342,17 @@ public class ResolveToolwindow extends JPanel implements Disposable {
|
||||
if (receiverArgument.exists()) {
|
||||
builder.append("/").append(receiverArgument);
|
||||
}
|
||||
|
||||
|
||||
if (thisObject.exists()) {
|
||||
builder.append("/this=").append(thisObject);
|
||||
}
|
||||
|
||||
|
||||
if (thisObject.exists() || receiverArgument.exists()) {
|
||||
builder.append("/.");
|
||||
}
|
||||
}
|
||||
|
||||
private void setText(final String text) {
|
||||
private void setText(@NotNull final String text) {
|
||||
new WriteCommandAction(myProject) {
|
||||
@Override
|
||||
protected void run(Result result) throws Throwable {
|
||||
|
||||
@@ -35,7 +35,7 @@ public abstract class LongRunningReadTask<RequestInfo, ResultData> {
|
||||
INITIALIZED,
|
||||
STARTED,
|
||||
FINISHED,
|
||||
FINISHED_AND_PROCESSED
|
||||
FINISHED_WITH_ACTUAL_DATA
|
||||
}
|
||||
|
||||
private ProgressIndicator progressIndicator = null;
|
||||
@@ -45,7 +45,7 @@ public abstract class LongRunningReadTask<RequestInfo, ResultData> {
|
||||
protected LongRunningReadTask() {}
|
||||
|
||||
/** Should be executed in GUI thread */
|
||||
public boolean isShouldStartWithCancel(@Nullable LongRunningReadTask<RequestInfo, ResultData> previousTask) {
|
||||
public boolean shouldStart(@Nullable LongRunningReadTask<RequestInfo, ResultData> previousTask) {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
|
||||
if (currentState != State.INITIALIZED) {
|
||||
@@ -60,20 +60,19 @@ public abstract class LongRunningReadTask<RequestInfo, ResultData> {
|
||||
}
|
||||
|
||||
if (requestInfo == null) {
|
||||
if (previousTask != null && (previousTask.currentState == State.FINISHED_AND_PROCESSED || previousTask.currentState == State.FINISHED)) {
|
||||
if (previousTask != null && (previousTask.currentState == State.FINISHED_WITH_ACTUAL_DATA || previousTask.currentState == State.FINISHED)) {
|
||||
previousTask.hideResultOnInvalidLocation();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (previousTask != null) {
|
||||
if (previousTask.currentState == State.STARTED) {
|
||||
// Start new task only if previous isn't working on similar request
|
||||
return !requestInfo.equals(previousTask.requestInfo);
|
||||
}
|
||||
else if (previousTask.currentState == State.FINISHED_AND_PROCESSED) {
|
||||
else if (previousTask.currentState == State.FINISHED_WITH_ACTUAL_DATA) {
|
||||
if (requestInfo.equals(previousTask.requestInfo)) {
|
||||
return false;
|
||||
}
|
||||
@@ -144,7 +143,7 @@ public abstract class LongRunningReadTask<RequestInfo, ResultData> {
|
||||
if (resultData != null) {
|
||||
RequestInfo actualInfo = prepareRequestInfo();
|
||||
if (requestInfo.equals(actualInfo)) {
|
||||
currentState = State.FINISHED_AND_PROCESSED;
|
||||
currentState = State.FINISHED_WITH_ACTUAL_DATA;
|
||||
onResultReady(actualInfo, resultData);
|
||||
}
|
||||
}
|
||||
@@ -172,6 +171,8 @@ public abstract class LongRunningReadTask<RequestInfo, ResultData> {
|
||||
|
||||
/**
|
||||
* Executed in GUI Thread.
|
||||
*
|
||||
* @return null if current request is invalid and task shouldn't be executed.
|
||||
*/
|
||||
@Nullable
|
||||
protected abstract RequestInfo prepareRequestInfo();
|
||||
|
||||
Reference in New Issue
Block a user