XDebugger: compare value with clipboard action (IDEA-89243), finish 'Load value' progress if evaluation failed

This commit is contained in:
Kirill Safonov
2012-08-01 22:05:17 +04:00
parent 527a8eadee
commit af10749df3
11 changed files with 276 additions and 183 deletions
@@ -15,75 +15,17 @@
*/
package com.intellij.debugger.actions;
import com.intellij.debugger.DebuggerBundle;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
import com.intellij.openapi.diff.*;
import com.intellij.openapi.ide.CopyPasteManager;
import com.intellij.openapi.diff.DiffManager;
import com.intellij.openapi.diff.actions.ClipboardVsValueContents;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.SystemInfo;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
/**
* @author Jeka
*/
public class CompareValueWithClipboardAction extends BaseValueAction {
protected void processText(final Project project, final String text, DebuggerTreeNodeImpl node, DebuggerContextImpl debuggerContext) {
DiffManager.getInstance().getDiffTool().show(new ClipboardSelectionContents(text, project));
}
private static class ClipboardSelectionContents extends DiffRequest {
private DiffContent[] myContents = null;
private final String myValue;
public ClipboardSelectionContents(String value, Project project) {
super(project);
myValue = value;
}
public String[] getContentTitles() {
return new String[] {
DiffBundle.message("diff.content.clipboard.content.title"),
DebuggerBundle.message("diff.content.selected.value")
};
}
@Override
public boolean isSafeToCallFromUpdate() {
return !SystemInfo.isMac;
}
@NotNull
public DiffContent[] getContents() {
if (myContents != null) return myContents;
DiffContent clipboardContent = createClipboardContent();
if (clipboardContent == null) clipboardContent = new SimpleContent("");
myContents = new DiffContent[2];
myContents[0] = clipboardContent;
myContents[1] = new SimpleContent(myValue);
return myContents;
}
public String getWindowTitle() {
return DebuggerBundle.message("diff.clipboard.vs.value.dialog.title");
}
@Nullable
private static DiffContent createClipboardContent() {
Transferable content = CopyPasteManager.getInstance().getContents();
if (content != null) {
try {
String text = (String) (content.getTransferData(DataFlavor.stringFlavor));
return text != null ? new SimpleContent(text) : null;
} catch (Exception ignored) { }
}
return null;
}
DiffManager.getInstance().getDiffTool().show(new ClipboardVsValueContents(text, project));
}
}
@@ -0,0 +1,86 @@
/*
* Copyright 2000-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 com.intellij.openapi.diff.actions;
import com.intellij.openapi.diff.DiffBundle;
import com.intellij.openapi.diff.DiffContent;
import com.intellij.openapi.diff.DiffRequest;
import com.intellij.openapi.diff.SimpleContent;
import com.intellij.openapi.ide.CopyPasteManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.SystemInfo;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
/**
* @author Jeka
*/
public class ClipboardVsValueContents extends DiffRequest {
private DiffContent[] myContents = null;
private final String myValue;
public ClipboardVsValueContents(String value, Project project) {
super(project);
myValue = value;
}
public String[] getContentTitles() {
return new String[]{
DiffBundle.message("diff.content.clipboard.content.title"),
DiffBundle.message("diff.content.selected.value")
};
}
@Override
public boolean isSafeToCallFromUpdate() {
return !SystemInfo.isMac;
}
@NotNull
public DiffContent[] getContents() {
if (myContents != null) return myContents;
DiffContent clipboardContent = createClipboardContent();
if (clipboardContent == null) clipboardContent = new SimpleContent("");
myContents = new DiffContent[2];
myContents[0] = clipboardContent;
myContents[1] = new SimpleContent(myValue);
return myContents;
}
public String getWindowTitle() {
return DiffBundle.message("diff.clipboard.vs.value.dialog.title");
}
@Nullable
public static DiffContent createClipboardContent() {
Transferable content = CopyPasteManager.getInstance().getContents();
if (content != null) {
try {
String text = (String)(content.getTransferData(DataFlavor.stringFlavor));
return text != null ? new SimpleContent(text) : null;
}
catch (Exception ignored) {
}
}
return null;
}
}
@@ -22,16 +22,12 @@ import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.SelectionModel;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.ide.CopyPasteManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.TextRange;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
public class CompareClipboardWithSelection extends BaseDiffAction {
@Nullable
protected DiffRequest getDiffData(DataContext dataContext) {
@@ -68,7 +64,7 @@ public class CompareClipboardWithSelection extends BaseDiffAction {
@NotNull
public DiffContent[] getContents() {
if (myContents != null) return myContents;
DiffContent clipboardContent = createClipboardContent();
DiffContent clipboardContent = ClipboardVsValueContents.createClipboardContent();
if (clipboardContent == null) clipboardContent = new SimpleContent("");
myContents = new DiffContent[2];
myContents[0] = clipboardContent;
@@ -97,16 +93,5 @@ public class CompareClipboardWithSelection extends BaseDiffAction {
}
}
@Nullable
private static DiffContent createClipboardContent() {
Transferable content = CopyPasteManager.getInstance().getContents();
if (content != null) {
try {
String text = (String) (content.getTransferData(DataFlavor.stringFlavor));
return text != null ? new SimpleContent(text) : null;
} catch (Exception ignored) { }
}
return null;
}
}
}
@@ -857,6 +857,8 @@ action.XDebugger.SetValue.text=Set Value...
action.XDebugger.SetValue.description=Modify value of selected node
action.XDebugger.CopyValue.text=Copy Value
action.XDebugger.CopyValue.description=Copy value of selected node to clipboard
action.XDebugger.CompareValueWithClipboard.text=Compare Value with Clipboard
action.XDebugger.CompareValueWithClipboard.description=Compare value of selected node with clipboard text
action.XDebugger.CopyName.text=Copy Name
action.XDebugger.CopyName.description=Copy name of selected node to clipboard
action.XDebugger.JumpToSource.text=Jump To Source
@@ -879,6 +881,7 @@ action.Debugger.CopyValue.text=Copy Value
action.Debugger.ViewText.text=View Text
action.Debugger.ViewText.description=View text value of selected node in a separate pane
action.Debugger.CompareValueWithClipboard.text=Compare Value with Clipboard
action.Debugger.CompareValueWithClipboard.description=Compare value of selected node with clipboard text
action.Debugger.CustomizeContextView.text=Customize Data Views...
action.Debugger.CustomizeThreadsView.text=Customize Threads View...
action.Debugger.NewWatch.text=New Watch...
@@ -78,3 +78,5 @@ diff.type.changed.name=Changed
diff.type.deleted.name=Deleted
diff.type.conflict.name=Conflict
diff.type.none.name=None
diff.content.selected.value=Selected Value
diff.clipboard.vs.value.dialog.title=Clipboard vs Selected Value
@@ -93,4 +93,6 @@ debugger.threads.not.available=Threads are not available
debugger.variables.not.available=Variables are not available
debugger.no.watches=No watches
debugger.no.breakpoints=No breakpoints
debugger.no.results=Type an expression and press 'Enter'
debugger.no.results=Type an expression and press 'Enter'
load.value.task.error=Cannot load value\: {0}
load.value.task.text=Loading value
@@ -650,6 +650,7 @@
<group id="XDebugger.Actions">
<action id="XDebugger.SetValue" class="com.intellij.xdebugger.impl.ui.tree.actions.XSetValueAction"/>
<action id="XDebugger.CopyValue" class="com.intellij.xdebugger.impl.ui.tree.actions.XCopyValueAction"/>
<action id="XDebugger.CompareValueWithClipboard" class="com.intellij.xdebugger.impl.ui.tree.actions.XCompareWithClipboardAction"/>
<action id="XDebugger.CopyName" class="com.intellij.xdebugger.impl.ui.tree.actions.XCopyNameAction"/>
<action id="XDebugger.Inspect" class="com.intellij.xdebugger.impl.ui.tree.actions.XInspectAction"/>
<action id="XDebugger.JumpToSource" class="com.intellij.xdebugger.impl.ui.tree.actions.XJumpToSourceAction"/>
@@ -690,6 +691,7 @@
<reference ref="XDebugger.Inspect"/>
<reference ref="XDebugger.SetValue"/>
<reference ref="XDebugger.CopyValue"/>
<reference ref="XDebugger.CompareValueWithClipboard"/>
<reference ref="XDebugger.CopyName"/>
<reference ref="Debugger.MarkObject"/>
</group>
@@ -0,0 +1,38 @@
/*
* Copyright 2000-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 com.intellij.xdebugger.impl.ui.tree.actions;
import com.intellij.openapi.diff.DiffManager;
import com.intellij.openapi.diff.actions.ClipboardVsValueContents;
import com.intellij.openapi.project.Project;
import com.intellij.util.ui.UIUtil;
/**
* User: ksafonov
*/
public class XCompareWithClipboardAction extends XFetchValueActionBase {
@Override
protected void handle(final Project project, final String value) {
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
public void run() {
DiffManager.getInstance().getDiffTool().show(new ClipboardVsValueContents(value, project));
}
});
}
}
@@ -15,116 +15,17 @@
*/
package com.intellij.xdebugger.impl.ui.tree.actions;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.ide.CopyPasteManager;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
import com.intellij.util.Alarm;
import com.intellij.util.concurrency.Semaphore;
import com.intellij.xdebugger.XDebugSession;
import com.intellij.xdebugger.frame.XFullValueEvaluator;
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.intellij.openapi.project.Project;
import java.awt.*;
import java.awt.datatransfer.StringSelection;
/**
* @author nik
*/
public class XCopyValueAction extends XDebuggerTreeActionBase {
protected void perform(final XValueNodeImpl node, @NotNull final String nodeName, final AnActionEvent e) {
XFullValueEvaluator fullValueEvaluator = node.getFullValueEvaluator();
if (fullValueEvaluator == null) {
String value = node.getValue();
copyToClipboard(value);
return;
}
final XDebugSession session = node.getTree().getSession();
startCopying(fullValueEvaluator, session);
}
private static void startCopying(@NotNull XFullValueEvaluator fullValueEvaluator, final @NotNull XDebugSession session) {
final CopyValueEvaluationCallback callback = new CopyValueEvaluationCallback(session);
fullValueEvaluator.startEvaluation(callback);
new Alarm().addRequest(new Runnable() {
@Override
public void run() {
callback.showProgress();
}
}, 500);
}
private static void copyToClipboard(String value) {
public class XCopyValueAction extends XFetchValueActionBase {
@Override
protected void handle(final Project project, final String value) {
CopyPasteManager.getInstance().setContents(new StringSelection(value));
}
protected boolean isEnabled(final XValueNodeImpl node) {
return super.isEnabled(node) && node.getValue() != null;
}
private static class CopyValueEvaluationCallback implements XFullValueEvaluator.XFullValueEvaluationCallback {
private final XDebugSession mySession;
private volatile boolean myEvaluated;
private volatile boolean myCanceled;
private Semaphore mySemaphore;
public CopyValueEvaluationCallback(XDebugSession session) {
mySession = session;
mySemaphore = new Semaphore();
mySemaphore.down();
}
@Override
public void evaluated(@NotNull String fullValue) {
copyToClipboard(fullValue);
myEvaluated = true;
mySemaphore.up();
}
@Override
public void evaluated(@NotNull String fullValue, @Nullable Font font) {
evaluated(fullValue);
}
@Override
public void errorOccurred(@NotNull String errorMessage) {
mySession.reportError("Cannot copy value: " + errorMessage);
}
@Override
public boolean isObsolete() {
return myCanceled;
}
public void showProgress() {
if (myEvaluated || mySession.isStopped()) return;
new Task.Backgroundable(mySession.getProject(), "Copying Value") {
@Override
public void run(@NotNull ProgressIndicator indicator) {
indicator.setIndeterminate(true);
indicator.setText("Fetching value...");
int i = 0;
while (!myCanceled && !myEvaluated) {
indicator.checkCanceled();
indicator.setFraction(((i++)%100)*0.01);
mySemaphore.waitFor(300);
}
}
@Override
public boolean shouldStartInBackground() {
return false;
}
@Override
public void onCancel() {
myCanceled = true;
}
}.queue();
}
}
}
@@ -0,0 +1,134 @@
/*
* Copyright 2000-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 com.intellij.xdebugger.impl.ui.tree.actions;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.util.Alarm;
import com.intellij.util.concurrency.Semaphore;
import com.intellij.xdebugger.XDebugSession;
import com.intellij.xdebugger.XDebuggerBundle;
import com.intellij.xdebugger.frame.XFullValueEvaluator;
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
/**
* User: ksafonov
*/
public abstract class XFetchValueActionBase extends XDebuggerTreeActionBase {
protected abstract void handle(final Project project, final String value);
protected void perform(final XValueNodeImpl node, @NotNull final String nodeName, final AnActionEvent e) {
XFullValueEvaluator fullValueEvaluator = node.getFullValueEvaluator();
if (fullValueEvaluator == null) {
String value = node.getValue();
handle(node.getTree().getProject(), value);
return;
}
final XDebugSession session = node.getTree().getSession();
startFetchingValue(fullValueEvaluator, session);
}
private void startFetchingValue(@NotNull XFullValueEvaluator fullValueEvaluator,
final @NotNull XDebugSession session) {
final CopyValueEvaluationCallback callback = new CopyValueEvaluationCallback(session);
fullValueEvaluator.startEvaluation(callback);
new Alarm().addRequest(new Runnable() {
@Override
public void run() {
callback.showProgress();
}
}, 500);
}
protected boolean isEnabled(final XValueNodeImpl node) {
return super.isEnabled(node) && node.getValue() != null;
}
private class CopyValueEvaluationCallback implements XFullValueEvaluator.XFullValueEvaluationCallback {
private final XDebugSession mySession;
private volatile boolean myEvaluated;
private volatile boolean myCanceled;
private Semaphore mySemaphore;
public CopyValueEvaluationCallback(XDebugSession session) {
mySession = session;
mySemaphore = new Semaphore();
mySemaphore.down();
}
@Override
public void evaluated(@NotNull String fullValue) {
handle(mySession.getProject(), fullValue);
evaluationComplete();
}
@Override
public void evaluated(@NotNull String fullValue, @Nullable Font font) {
evaluated(fullValue);
}
@Override
public void errorOccurred(@NotNull String errorMessage) {
mySession.reportError(XDebuggerBundle.message("load.value.task.error", errorMessage));
evaluationComplete();
}
private void evaluationComplete() {
myEvaluated = true;
mySemaphore.up();
}
@Override
public boolean isObsolete() {
return myCanceled;
}
public void showProgress() {
if (myEvaluated || mySession.isStopped()) return;
new Task.Backgroundable(mySession.getProject(), XDebuggerBundle.message("load.value.task.text")) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
indicator.setIndeterminate(true);
int i = 0;
while (!myCanceled && !myEvaluated) {
indicator.checkCanceled();
indicator.setFraction(((i++) % 100) * 0.01);
mySemaphore.waitFor(300);
}
}
@Override
public boolean shouldStartInBackground() {
return false;
}
@Override
public void onCancel() {
myCanceled = true;
}
}.queue();
}
}
}
@@ -443,6 +443,4 @@ frame.panel.frames.not.available=Frames not available for unsuspended thread
error.corrupt.debug.info=Debug info might be corrupt: {0}
action.kill.process.text=Kill Process
action.kill.process.description=Forcibly terminate debugged application
diff.clipboard.vs.value.dialog.title=Clipboard vs Selected Value
diff.content.selected.value=Selected Value
evaluation.error.unknown.method.return.type=Cannot resolve method return type: {0}