/*
* Copyright 2013-2016 Sergey Ignatov, Alexander Zolotov, Florin Patan
*
* 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 com.goide.dlv;
import com.goide.GoIcons;
import com.goide.dlv.protocol.DlvApi;
import com.goide.dlv.protocol.DlvRequest;
import com.goide.psi.*;
import com.goide.sdk.GoSdkService;
import com.intellij.execution.configurations.ModuleBasedConfiguration;
import com.intellij.execution.configurations.RunProfile;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.ui.ColoredTextContainer;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.xdebugger.XDebuggerUtil;
import com.intellij.xdebugger.XSourcePosition;
import com.intellij.xdebugger.evaluation.XDebuggerEvaluator;
import com.intellij.xdebugger.frame.XCompositeNode;
import com.intellij.xdebugger.frame.XStackFrame;
import com.intellij.xdebugger.frame.XValue;
import com.intellij.xdebugger.frame.XValueChildrenList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.concurrency.Promise;
import javax.swing.*;
class DlvStackFrame extends XStackFrame {
private final DlvDebugProcess myProcess;
private final DlvApi.Location myLocation;
private final DlvCommandProcessor myProcessor;
private final int myId;
public DlvStackFrame(@NotNull DlvDebugProcess process,
@NotNull DlvApi.Location location,
@NotNull DlvCommandProcessor processor,
int id) {
myProcess = process;
myLocation = location;
myProcessor = processor;
myId = id;
}
@Nullable
@Override
public XDebuggerEvaluator getEvaluator() {
return new XDebuggerEvaluator() {
@Override
public void evaluate(@NotNull String expression,
@NotNull XEvaluationCallback callback,
@Nullable XSourcePosition expressionPosition) {
myProcessor.send(new DlvRequest.EvalSymbol(expression, myId))
.done(variable -> callback.evaluated(createXValue(variable, AllIcons.Debugger.Watch)))
.rejected(throwable -> callback.errorOccurred(throwable.getMessage()));
}
@Nullable
private PsiElement findElementAt(@Nullable PsiFile file, int offset) {
return file != null ? file.findElementAt(offset) : null;
}
@Nullable
@Override
public TextRange getExpressionRangeAtOffset(@NotNull Project project,
@NotNull Document document,
int offset,
boolean sideEffectsAllowed) {
Ref<TextRange> currentRange = Ref.create(null);
PsiDocumentManager.getInstance(project).commitAndRunReadAction(() -> {
try {
PsiElement elementAtCursor = findElementAt(PsiDocumentManager.getInstance(project).getPsiFile(document), offset);
GoTypeOwner e = PsiTreeUtil.getParentOfType(elementAtCursor,
GoExpression.class,
GoVarDefinition.class,
GoConstDefinition.class,
GoParamDefinition.class);
if (e != null) {
currentRange.set(e.getTextRange());
}
}
catch (IndexNotReadyException ignored) {
}
});
return currentRange.get();
}
};
}
@NotNull
private XValue createXValue(@NotNull DlvApi.Variable variable, @Nullable Icon icon) {
return new DlvXValue(myProcess, variable, myProcessor, myId, icon);
}
@Nullable
@Override
public XSourcePosition getSourcePosition() {
VirtualFile file = findFile();
return file == null ? null : XDebuggerUtil.getInstance().createPosition(file, myLocation.line - 1);
}
@Nullable
private VirtualFile findFile() {
String url = myLocation.file;
VirtualFile file = LocalFileSystem.getInstance().findFileByPath(url);
if (file == null && SystemInfo.isWindows) {
Project project = myProcess.getSession().getProject();
RunProfile profile = myProcess.getSession().getRunProfile();
Module module = profile instanceof ModuleBasedConfiguration ? ((ModuleBasedConfiguration)profile).getConfigurationModule().getModule() : null;
String sdkHomePath = GoSdkService.getInstance(project).getSdkHomePath(module);
if (sdkHomePath == null) return null;
String newUrl = StringUtil.replaceIgnoreCase(url, "c:/go", sdkHomePath);
return LocalFileSystem.getInstance().findFileByPath(newUrl);
}
return file;
}
@Override
public void customizePresentation(@NotNull ColoredTextContainer component) {
super.customizePresentation(component);
component.append(" at " + myLocation.function.name, SimpleTextAttributes.REGULAR_ATTRIBUTES);
component.setIcon(AllIcons.Debugger.StackFrame);
}
@NotNull
private <T> Promise<T> send(@NotNull DlvRequest<T> request) {
return DlvDebugProcess.send(request, myProcessor);
}
@Override
public void computeChildren(@NotNull XCompositeNode node) {
send(new DlvRequest.ListLocalVars(myId)).done(variables -> {
XValueChildrenList xVars = new XValueChildrenList(variables.size());
for (DlvApi.Variable v : variables) xVars.add(v.name, createXValue(v, GoIcons.VARIABLE));
send(new DlvRequest.ListFunctionArgs(myId)).done(args -> {
for (DlvApi.Variable v : args) xVars.add(v.name, createXValue(v, GoIcons.PARAMETER));
node.addChildren(xVars, true);
});
});
}
}