package com.jetbrains.lang.dart.ide.runner.server.vmService.frame;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.xdebugger.XSourcePosition;
import com.intellij.xdebugger.evaluation.ExpressionInfo;
import com.intellij.xdebugger.evaluation.XDebuggerEvaluator;
import com.jetbrains.lang.dart.ide.runner.server.vmService.DartVmServiceDebugProcess;
import com.jetbrains.lang.dart.psi.*;
import org.dartlang.vm.service.element.Frame;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class DartVmServiceEvaluator extends XDebuggerEvaluator {
private static final Pattern ERROR_PATTERN = Pattern.compile("Error:.* line \\d+ pos \\d+: (.+)");
@NotNull private final DartVmServiceDebugProcess myDebugProcess;
@NotNull private final String myIsolateId;
@NotNull private final Frame myFrame;
public DartVmServiceEvaluator(@NotNull final DartVmServiceDebugProcess debugProcess,
@NotNull final String isolateId,
@NotNull final Frame vmFrame) {
myDebugProcess = debugProcess;
myIsolateId = isolateId;
myFrame = vmFrame;
}
@NotNull
public static String getPresentableError(@NotNull final String rawError) {
//Error: Unhandled exception:
//No top-level getter 'foo' declared.
//
//NoSuchMethodError: method not found: 'foo'
//Receiver: top-level
//Arguments: [...]
//#0 NoSuchMethodError._throwNew (dart:core-patch/errors_patch.dart:176)
//#1 _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:260)
//#2 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:142)
//Error: '': error: line 1 pos 9: receiver 'this' is not in scope
//() => 1+this.foo();
// ^
final List<String> lines = StringUtil.split(StringUtil.convertLineSeparators(rawError), "\n");
if (!lines.isEmpty()) {
if ((lines.get(0).equals("Error: Unhandled exception:") || lines.get(0).equals("Unhandled exception:")) && lines.size() > 1) {
return lines.get(1);
}
final Matcher matcher = ERROR_PATTERN.matcher(lines.get(0));
if (matcher.find()) {
return matcher.group(1);
}
}
return "Cannot evaluate";
}
@Nullable
public static ExpressionInfo getExpressionInfo(@NotNull final PsiElement contextElement) {
// todo if sideEffectsAllowed return method call like "foo()", not only "foo"
/* WEB-11715
dart psi: notes.text
REFERENCE_EXPRESSION
REFERENCE_EXPRESSION "notes"
PsiElement(.) "."
REFERENCE_EXPRESSION "text"
*/
// find topmost reference, but stop if argument list found
DartReference reference = null;
PsiElement element = contextElement;
while (true) {
if (element instanceof DartReference) {
reference = (DartReference)element;
}
element = element.getParent();
if (element == null ||
// int.parse(slider.value) - we must return reference expression "slider.value", but not the whole expression
element instanceof DartArgumentList ||
// "${seeds} seeds" - we must return only "seeds"
element instanceof DartLongTemplateEntry ||
element instanceof DartCallExpression ||
element instanceof DartFunctionBody || element instanceof IDartBlock) {
break;
}
}
if (reference != null) {
TextRange textRange = reference.getTextRange();
// note<CURSOR>s.text - the whole reference expression is notes.txt, but we must return only notes
int endOffset = contextElement.getTextRange().getEndOffset();
if (textRange.getEndOffset() != endOffset) {
textRange = new TextRange(textRange.getStartOffset(), endOffset);
}
return new ExpressionInfo(textRange);
}
PsiElement parent = contextElement.getParent();
return parent instanceof DartId ? new ExpressionInfo(parent.getTextRange()) : null;
}
@Override
public boolean isCodeFragmentEvaluationSupported() {
return false;
}
@Override
public void evaluate(@NotNull final String expression,
@NotNull final XEvaluationCallback callback,
@Nullable final XSourcePosition expressionPosition) {
myDebugProcess.getVmServiceWrapper().evaluateInFrame(myIsolateId, myFrame, expression, callback);
}
@Nullable
@Override
public ExpressionInfo getExpressionInfoAtOffset(@NotNull final Project project,
@NotNull final Document document,
final int offset,
final boolean sideEffectsAllowed) {
final PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document);
final PsiElement contextElement = psiFile == null ? null : psiFile.findElementAt(offset);
return contextElement == null ? null : getExpressionInfo(contextElement);
}
}