|
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
|
2 // Use of this source code is governed by a BSD-style license that can be |
|
3 // found in the LICENSE file. |
|
4 |
|
5 package org.chromium.debug.ui.actions; |
|
6 |
|
7 import org.chromium.debug.core.model.StackFrame; |
|
8 import org.chromium.debug.ui.ChromiumDebugUIPlugin; |
|
9 import org.chromium.debug.ui.JsEvalContextManager; |
|
10 import org.chromium.debug.ui.editors.JavascriptUtil; |
|
11 import org.chromium.sdk.CallFrame; |
|
12 import org.chromium.sdk.JsVariable; |
|
13 import org.eclipse.debug.core.model.IExpression; |
|
14 import org.eclipse.debug.ui.DebugPopup; |
|
15 import org.eclipse.debug.ui.InspectPopupDialog; |
|
16 import org.eclipse.jface.action.IAction; |
|
17 import org.eclipse.jface.text.ITextSelection; |
|
18 import org.eclipse.jface.text.ITextViewer; |
|
19 import org.eclipse.jface.viewers.ISelection; |
|
20 import org.eclipse.jface.viewers.ISelectionProvider; |
|
21 import org.eclipse.swt.custom.StyledText; |
|
22 import org.eclipse.swt.graphics.GC; |
|
23 import org.eclipse.swt.graphics.Point; |
|
24 import org.eclipse.swt.widgets.Control; |
|
25 import org.eclipse.swt.widgets.Shell; |
|
26 import org.eclipse.ui.IEditorActionDelegate; |
|
27 import org.eclipse.ui.IEditorPart; |
|
28 import org.eclipse.ui.IPartListener; |
|
29 import org.eclipse.ui.IViewActionDelegate; |
|
30 import org.eclipse.ui.IViewPart; |
|
31 import org.eclipse.ui.IWorkbenchPage; |
|
32 import org.eclipse.ui.IWorkbenchPart; |
|
33 import org.eclipse.ui.IWorkbenchWindow; |
|
34 import org.eclipse.ui.IWorkbenchWindowActionDelegate; |
|
35 import org.eclipse.ui.texteditor.ITextEditor; |
|
36 |
|
37 /** |
|
38 * Action for inspecting a JavaScript snippet. |
|
39 */ |
|
40 public class JsInspectSnippetAction implements IEditorActionDelegate, |
|
41 IWorkbenchWindowActionDelegate, IPartListener, IViewActionDelegate, CallFrame.EvaluateCallback { |
|
42 |
|
43 private static final String ACTION_DEFINITION_ID = "org.chromium.debug.ui.commands.Inspect"; //$NON-NLS-1$ |
|
44 |
|
45 private IWorkbenchWindow window; |
|
46 |
|
47 private IWorkbenchPart targetPart; |
|
48 |
|
49 private IAction action; |
|
50 |
|
51 private String selectedText; |
|
52 |
|
53 private IExpression expression; |
|
54 |
|
55 private ITextEditor textEditor; |
|
56 |
|
57 private ISelection originalSelection; |
|
58 |
|
59 public void setActiveEditor(IAction action, IEditorPart targetEditor) { |
|
60 this.action = action; |
|
61 setTargetPart(targetEditor); |
|
62 } |
|
63 |
|
64 public void run(IAction action) { |
|
65 updateAction(); |
|
66 run(); |
|
67 } |
|
68 |
|
69 public void selectionChanged(IAction action, ISelection selection) { |
|
70 this.action = action; |
|
71 } |
|
72 |
|
73 public void dispose() { |
|
74 IWorkbenchWindow win = getWindow(); |
|
75 if (win != null) { |
|
76 win.getPartService().removePartListener(this); |
|
77 } |
|
78 } |
|
79 |
|
80 private IWorkbenchWindow getWindow() { |
|
81 return window; |
|
82 } |
|
83 |
|
84 public void init(IWorkbenchWindow window) { |
|
85 this.window = window; |
|
86 IWorkbenchPage page = window.getActivePage(); |
|
87 if (page != null) { |
|
88 setTargetPart(page.getActivePart()); |
|
89 } |
|
90 window.getPartService().addPartListener(this); |
|
91 } |
|
92 |
|
93 public void partActivated(IWorkbenchPart part) { |
|
94 setTargetPart(part); |
|
95 } |
|
96 |
|
97 public void partBroughtToTop(IWorkbenchPart part) { |
|
98 |
|
99 } |
|
100 |
|
101 public void partClosed(IWorkbenchPart part) { |
|
102 if (part == getTargetPart()) { |
|
103 setTargetPart(null); |
|
104 } |
|
105 } |
|
106 |
|
107 private IWorkbenchPart getTargetPart() { |
|
108 return targetPart; |
|
109 } |
|
110 |
|
111 public void partDeactivated(IWorkbenchPart part) { |
|
112 } |
|
113 |
|
114 public void partOpened(IWorkbenchPart part) { |
|
115 } |
|
116 |
|
117 private void setTargetPart(IWorkbenchPart part) { |
|
118 this.targetPart = part; |
|
119 } |
|
120 |
|
121 public void init(IViewPart view) { |
|
122 setTargetPart(view); |
|
123 } |
|
124 |
|
125 private void updateAction() { |
|
126 if (action != null) { |
|
127 retrieveSelection(); |
|
128 } |
|
129 } |
|
130 |
|
131 protected ISelection getTargetSelection() { |
|
132 IWorkbenchPart part = getTargetPart(); |
|
133 if (part != null) { |
|
134 ISelectionProvider provider = part.getSite().getSelectionProvider(); |
|
135 if (provider != null) { |
|
136 return provider.getSelection(); |
|
137 } |
|
138 } |
|
139 return null; |
|
140 } |
|
141 |
|
142 private StackFrame getStackFrameContext() { |
|
143 IWorkbenchPart part = getTargetPart(); |
|
144 return getStackFrameForPart(part); |
|
145 } |
|
146 |
|
147 private StackFrame getStackFrameForPart(IWorkbenchPart part) { |
|
148 StackFrame frame = part == null |
|
149 ? JsEvalContextManager.getStackFrameFor(getWindow()) |
|
150 : JsEvalContextManager.getStackFrameFor(part); |
|
151 return frame; |
|
152 } |
|
153 |
|
154 private void run() { |
|
155 getStackFrameContext().getCallFrame().evaluateAsync(getSelectedText(), this, null); |
|
156 } |
|
157 |
|
158 protected String getSelectedText() { |
|
159 return selectedText; |
|
160 } |
|
161 |
|
162 protected Shell getShell() { |
|
163 if (getTargetPart() != null) { |
|
164 return getTargetPart().getSite().getShell(); |
|
165 } |
|
166 return ChromiumDebugUIPlugin.getActiveWorkbenchShell(); |
|
167 } |
|
168 |
|
169 private void retrieveSelection() { |
|
170 ISelection targetSelection = getTargetSelection(); |
|
171 if (targetSelection instanceof ITextSelection) { |
|
172 ITextSelection ts = (ITextSelection) targetSelection; |
|
173 String text = ts.getText(); |
|
174 if (textHasContent(text)) { |
|
175 selectedText = text; |
|
176 } else if (getTargetPart() instanceof IEditorPart) { |
|
177 IEditorPart editor = (IEditorPart) getTargetPart(); |
|
178 if (editor instanceof ITextEditor) { |
|
179 selectedText = extractSurroundingWord(ts, (ITextEditor) editor); |
|
180 } |
|
181 } |
|
182 } |
|
183 } |
|
184 |
|
185 private String extractSurroundingWord(ITextSelection targetSelection, ITextEditor editor) { |
|
186 return JavascriptUtil.extractSurroundingJsIdentifier( |
|
187 editor.getDocumentProvider().getDocument(editor.getEditorInput()), |
|
188 targetSelection.getOffset()); |
|
189 } |
|
190 |
|
191 private boolean textHasContent(String text) { |
|
192 return text != null && JavascriptUtil.ID_PATTERN.matcher(text).find(); |
|
193 } |
|
194 |
|
195 public void success(JsVariable var) { |
|
196 if (ChromiumDebugUIPlugin.getDefault() == null) { |
|
197 return; |
|
198 } |
|
199 if (var != null) { |
|
200 if (ChromiumDebugUIPlugin.getDisplay().isDisposed()) { |
|
201 return; |
|
202 } |
|
203 displayResult(var, null); |
|
204 } |
|
205 } |
|
206 |
|
207 public void failure(String errorMessage) { |
|
208 displayResult(null, errorMessage); |
|
209 } |
|
210 |
|
211 protected void displayResult(final JsVariable var, String errorMessage) { |
|
212 IWorkbenchPart part = getTargetPart(); |
|
213 final StyledText styledText = getStyledText(part); |
|
214 if (styledText == null) { |
|
215 return; // TODO(apavlov): fix this when adding inspected variables |
|
216 } else { |
|
217 expression = new JsInspectExpression(getStackFrameContext(), selectedText, var, errorMessage); |
|
218 ChromiumDebugUIPlugin.getDisplay().asyncExec(new Runnable() { |
|
219 public void run() { |
|
220 showPopup(styledText); |
|
221 } |
|
222 }); |
|
223 } |
|
224 } |
|
225 |
|
226 protected void showPopup(StyledText textWidget) { |
|
227 IWorkbenchPart part = getTargetPart(); |
|
228 if (part instanceof ITextEditor) { |
|
229 textEditor = (ITextEditor) part; |
|
230 originalSelection = getTargetSelection(); |
|
231 } |
|
232 DebugPopup displayPopup = |
|
233 new InspectPopupDialog(getShell(), getPopupAnchor(textWidget), ACTION_DEFINITION_ID, |
|
234 expression) { |
|
235 @Override |
|
236 public boolean close() { |
|
237 boolean returnValue = super.close(); |
|
238 if (textEditor != null && originalSelection != null) { |
|
239 textEditor.getSelectionProvider().setSelection(originalSelection); |
|
240 textEditor = null; |
|
241 originalSelection = null; |
|
242 } |
|
243 return returnValue; |
|
244 } |
|
245 }; |
|
246 displayPopup.open(); |
|
247 } |
|
248 |
|
249 private StyledText getStyledText(IWorkbenchPart part) { |
|
250 ITextViewer viewer = (ITextViewer) part.getAdapter(ITextViewer.class); |
|
251 StyledText textWidget = null; |
|
252 if (viewer == null) { |
|
253 Control control = (Control) part.getAdapter(Control.class); |
|
254 if (control instanceof StyledText) { |
|
255 textWidget = (StyledText) control; |
|
256 } |
|
257 } else { |
|
258 textWidget = viewer.getTextWidget(); |
|
259 } |
|
260 return textWidget; |
|
261 } |
|
262 |
|
263 private static Point getPopupAnchor(StyledText textWidget) { |
|
264 if (textWidget != null) { |
|
265 Point docRange = textWidget.getSelectionRange(); |
|
266 int midOffset = docRange.x + (docRange.y / 2); |
|
267 Point point = textWidget.getLocationAtOffset(midOffset); |
|
268 point = textWidget.toDisplay(point); |
|
269 point.y += getFontHeight(textWidget); |
|
270 return point; |
|
271 } |
|
272 return null; |
|
273 } |
|
274 |
|
275 private static int getFontHeight(StyledText textWidget) { |
|
276 GC gc = new GC(textWidget); |
|
277 gc.setFont(textWidget.getFont()); |
|
278 int height = gc.getFontMetrics().getHeight(); |
|
279 gc.dispose(); |
|
280 return height; |
|
281 } |
|
282 |
|
283 } |