~ubuntu-branches/ubuntu/trusty/monodevelop/trusty-proposed

« back to all changes in this revision

Viewing changes to src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeActionEditorExtension.cs

  • Committer: Package Import Robot
  • Author(s): Jo Shields
  • Date: 2013-05-12 09:46:03 UTC
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: package-import@ubuntu.com-20130512094603-mad323bzcxvmcam0
Tags: upstream-4.0.5+dfsg
ImportĀ upstreamĀ versionĀ 4.0.5+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
33
33
using System.Linq;
34
34
using MonoDevelop.Refactoring;
35
35
using ICSharpCode.NRefactory;
 
36
using System.Threading;
 
37
using MonoDevelop.Core;
 
38
using ICSharpCode.NRefactory.CSharp;
36
39
 
37
40
namespace MonoDevelop.CodeActions
38
41
{
40
43
        {
41
44
                CodeActionWidget widget;
42
45
                uint quickFixTimeout;
43
 
                
 
46
 
 
47
                const int menuTimeout = 250;
 
48
                uint smartTagPopupTimeoutId;
 
49
                uint menuCloseTimeoutId;
 
50
                Menu codeActionMenu;
 
51
 
44
52
                public IEnumerable<CodeAction> Fixes {
45
53
                        get;
46
54
                        private set;
47
55
                }
 
56
 
 
57
                void CancelSmartTagPopupTimeout ()
 
58
                {
 
59
                        if (smartTagPopupTimeoutId != 0) {
 
60
                                GLib.Source.Remove (smartTagPopupTimeoutId);
 
61
                                smartTagPopupTimeoutId = 0;
 
62
                        }
 
63
                }
 
64
 
 
65
                void CancelMenuCloseTimer ()
 
66
                {
 
67
                        if (menuCloseTimeoutId != 0) {
 
68
                                GLib.Source.Remove (menuCloseTimeoutId);
 
69
                                menuCloseTimeoutId = 0;
 
70
                        }
 
71
                }
48
72
                
49
73
                void RemoveWidget ()
50
74
                {
51
 
                        if (widget == null)
52
 
                                return;
53
 
                        widget.Hide ();
 
75
                        if (widget != null) {
 
76
                                widget.Destroy ();
 
77
                                widget = null;
 
78
                        }
 
79
                        if (currentSmartTag != null) {
 
80
                                document.Editor.Document.RemoveMarker (currentSmartTag);
 
81
                                currentSmartTag = null;
 
82
                                currentSmartTagBegin = DocumentLocation.Empty;
 
83
                        }
 
84
                        CancelSmartTagPopupTimeout ();
 
85
 
54
86
                }
55
87
                
56
88
                public override void Dispose ()
57
89
                {
 
90
                        CancelMenuCloseTimer ();
58
91
                        CancelQuickFixTimer ();
59
92
                        document.Editor.SelectionChanged -= HandleSelectionChanged;
60
93
                        document.DocumentParsed -= HandleDocumentDocumentParsed;
61
 
                        if (widget != null) {
62
 
                                widget.Destroy ();
63
 
                                widget = null;
64
 
                        }
 
94
                        document.Editor.Parent.BeginHover -= HandleBeginHover;
 
95
                        RemoveWidget ();
65
96
                        base.Dispose ();
66
97
                }
67
 
                
 
98
                TextLocation loc;
68
99
                void CreateWidget (IEnumerable<CodeAction> fixes, TextLocation loc)
69
100
                {
70
 
                        Fixes = fixes;
71
 
                        if (!QuickTaskStrip.EnableFancyFeatures) {
72
 
                                RemoveWidget ();
73
 
                                return;
74
 
                        }
 
101
                        this.loc = loc;
75
102
                        var editor = document.Editor;
76
 
                        if (editor == null || editor.Parent == null || !editor.Parent.IsRealized) {
77
 
                                RemoveWidget ();
78
 
                                return;
79
 
                        }
80
 
                        if (document.ParsedDocument == null || document.ParsedDocument.IsInvalid) {
81
 
                                RemoveWidget ();
82
 
                                return;
83
 
                        }
84
 
                        if (!fixes.Any ()) {
85
 
                                ICSharpCode.NRefactory.Semantics.ResolveResult resolveResult;
86
 
                                ICSharpCode.NRefactory.CSharp.AstNode node;
87
 
                                if (ResolveCommandHandler.ResolveAt (document, out resolveResult, out node)) {
88
 
                                        var possibleNamespaces = ResolveCommandHandler.GetPossibleNamespaces (document, node, resolveResult);
89
 
                                        if (!possibleNamespaces.Any ()) {
90
 
                                                RemoveWidget ();
91
 
                                                return;
92
 
                                        }
93
 
                                } else {
94
 
                                        RemoveWidget ();
95
 
                                        return;
96
 
                                }
97
 
                        }
98
 
                        var container = editor.Parent.Parent as TextEditorContainer;
99
 
                        if (container == null) {
100
 
                                RemoveWidget ();
101
 
                                return;
102
 
                        }
 
103
                        var container = editor.Parent;
 
104
                        var point = editor.Parent.LocationToPoint (loc);
 
105
                        point.Y += (int)editor.LineHeight;
103
106
                        if (widget == null) {
104
107
                                widget = new CodeActionWidget (this, Document);
105
 
                                container.AddTopLevelWidget (widget,
106
 
                                        2 + (int)editor.Parent.TextViewMargin.XOffset,
107
 
                                        -2 + (int)editor.Parent.LineToY (document.Editor.Caret.Line));
 
108
                                container.AddTopLevelWidget (
 
109
                                        widget,
 
110
                                        point.X,
 
111
                                        point.Y
 
112
                                );
 
113
                                widget.Show ();
108
114
                        } else {
109
115
                                if (!widget.Visible)
110
116
                                        widget.Show ();
111
 
                                container.MoveTopLevelWidget (widget,
112
 
                                        2 + (int)editor.Parent.TextViewMargin.XOffset,
113
 
                                        -2 + (int)editor.Parent.LineToY (document.Editor.Caret.Line));
 
117
                                container.MoveTopLevelWidget (
 
118
                                        widget,
 
119
                                        point.X,
 
120
                                        point.Y
 
121
                                );
114
122
                        }
115
123
                        widget.SetFixes (fixes, loc);
116
124
                }
117
125
 
118
126
                public void CancelQuickFixTimer ()
119
127
                {
 
128
                        if (quickFixCancellationTokenSource != null)
 
129
                                quickFixCancellationTokenSource.Cancel ();
120
130
                        if (quickFixTimeout != 0) {
121
131
                                GLib.Source.Remove (quickFixTimeout);
122
132
                                quickFixTimeout = 0;
123
133
                        }
124
134
                }
125
135
 
 
136
                CancellationTokenSource quickFixCancellationTokenSource;
 
137
 
126
138
                public override void CursorPositionChanged ()
127
139
                {
128
140
                        CancelQuickFixTimer ();
129
 
 
130
 
                        if (QuickTaskStrip.EnableFancyFeatures &&  Document.ParsedDocument != null) {
 
141
                        if (QuickTaskStrip.EnableFancyFeatures &&  Document.ParsedDocument != null && !Debugger.DebuggingService.IsDebugging) {
 
142
                                quickFixCancellationTokenSource = new CancellationTokenSource ();
 
143
                                var token = quickFixCancellationTokenSource.Token;
131
144
                                quickFixTimeout = GLib.Timeout.Add (100, delegate {
132
145
                                        var loc = Document.Editor.Caret.Location;
133
 
                                        RefactoringService.QueueQuickFixAnalysis (Document, loc, delegate(List<CodeAction> fixes) {
 
146
                                        RefactoringService.QueueQuickFixAnalysis (Document, loc, token, delegate(List<CodeAction> fixes) {
 
147
                                                if (!fixes.Any ()) {
 
148
                                                        ICSharpCode.NRefactory.Semantics.ResolveResult resolveResult;
 
149
                                                        AstNode node;
 
150
                                                        if (ResolveCommandHandler.ResolveAt (document, out resolveResult, out node, token)) {
 
151
                                                                var possibleNamespaces = ResolveCommandHandler.GetPossibleNamespaces (document, node, ref resolveResult);
 
152
                                                                if (!possibleNamespaces.Any ()) {
 
153
                                                                        if (currentSmartTag != null)
 
154
                                                                                Application.Invoke (delegate { RemoveWidget (); });
 
155
                                                                        return;
 
156
                                                                }
 
157
                                                        } else {
 
158
                                                                if (currentSmartTag != null)
 
159
                                                                        Application.Invoke (delegate { RemoveWidget (); });
 
160
                                                                return;
 
161
                                                        }
 
162
                                                }
134
163
                                                Application.Invoke (delegate {
135
 
                                                        CreateWidget (fixes, loc);
 
164
                                                        if (token.IsCancellationRequested)
 
165
                                                                return;
 
166
                                                        CreateSmartTag (fixes, loc);
136
167
                                                        quickFixTimeout = 0;
137
168
                                                });
138
169
                                        });
143
174
                        }
144
175
                        base.CursorPositionChanged ();
145
176
                }
 
177
 
 
178
                class SmartTagMarker : TextSegmentMarker, IActionTextLineMarker
 
179
                {
 
180
                        CodeActionEditorExtension codeActionEditorExtension;
 
181
                        internal List<CodeAction> fixes;
 
182
                        DocumentLocation loc;
 
183
 
 
184
                        public SmartTagMarker (int offset, CodeActionEditorExtension codeActionEditorExtension, List<CodeAction> fixes, DocumentLocation loc) : base (offset, 0)
 
185
                        {
 
186
                                this.codeActionEditorExtension = codeActionEditorExtension;
 
187
                                this.fixes = fixes;
 
188
                                this.loc = loc;
 
189
                        }
 
190
 
 
191
                        public SmartTagMarker (int offset) : base (offset, 0)
 
192
                        {
 
193
                        }
 
194
                        const double tagMarkerWidth = 8;
 
195
                        const double tagMarkerHeight = 2;
 
196
                        public override void Draw (TextEditor editor, Cairo.Context cr, Pango.Layout layout, bool selected, int startOffset, int endOffset, double y, double startXPos, double endXPos)
 
197
                        {
 
198
                                var line = editor.GetLine (loc.Line);
 
199
                                var x = editor.ColumnToX (line, loc.Column) - editor.HAdjustment.Value + editor.TextViewMargin.XOffset + editor.TextViewMargin.TextStartPosition;
 
200
 
 
201
                                cr.Rectangle (Math.Floor (x) + 0.5, Math.Floor (y) + 0.5 + (line == editor.GetLineByOffset (startOffset) ? editor.LineHeight - tagMarkerHeight - 1 : 0), tagMarkerWidth * cr.LineWidth, tagMarkerHeight * cr.LineWidth);
 
202
 
 
203
                                if (HslColor.Brightness (editor.ColorStyle.PlainText.Background) < 0.5) {
 
204
                                        cr.Color = new Cairo.Color (0.8, 0.8, 1, 0.9);
 
205
                                } else {
 
206
                                        cr.Color = new Cairo.Color (0.2, 0.2, 1, 0.9);
 
207
                                }
 
208
                                cr.Stroke ();
 
209
                        }
 
210
 
 
211
                        #region IActionTextLineMarker implementation
 
212
 
 
213
                        bool IActionTextLineMarker.MousePressed (TextEditor editor, MarginMouseEventArgs args)
 
214
                        {
 
215
                                return false;
 
216
                        }
 
217
 
 
218
                        void IActionTextLineMarker.MouseHover (TextEditor editor, MarginMouseEventArgs args, TextLineMarkerHoverResult result)
 
219
                        {
 
220
                                if (args.Button != 0)
 
221
                                        return;
 
222
                                var line = editor.GetLine (loc.Line);
 
223
                                var x = editor.ColumnToX (line, loc.Column) - editor.HAdjustment.Value;
 
224
                                var y = editor.LineToY (line.LineNumber) - editor.VAdjustment.Value;
 
225
                                if (args.X - x >= 0 * editor.Options.Zoom && 
 
226
                                    args.X - x < tagMarkerWidth * editor.Options.Zoom && 
 
227
                                    y - args.Y < (tagMarkerHeight) * editor.Options.Zoom) {
 
228
                                        Popup ();
 
229
                                } else {
 
230
                                        codeActionEditorExtension.CancelSmartTagPopupTimeout ();
 
231
                                }
 
232
                        }
 
233
 
 
234
                        public void Popup ()
 
235
                        {
 
236
                                codeActionEditorExtension.smartTagPopupTimeoutId = GLib.Timeout.Add (menuTimeout, delegate {
 
237
                                        codeActionEditorExtension.CreateWidget (fixes, loc);
 
238
                                        codeActionEditorExtension.widget.PopupQuickFixMenu (menu => {
 
239
                                                codeActionEditorExtension.codeActionMenu = menu;
 
240
                                                menu.MotionNotifyEvent += (o, args) => {
 
241
                                                        if (args.Event.Window == codeActionEditorExtension.Editor.Parent.TextArea.GdkWindow) {
 
242
                                                                codeActionEditorExtension.StartMenuCloseTimer ();
 
243
                                                        } else {
 
244
                                                                codeActionEditorExtension.CancelMenuCloseTimer ();
 
245
                                                        }
 
246
                                                };
 
247
                                        });
 
248
                                        codeActionEditorExtension.widget.Destroy ();
 
249
                                        codeActionEditorExtension.smartTagPopupTimeoutId = 0;
 
250
                                        return false;
 
251
                                });
 
252
                        }
 
253
                        #endregion
 
254
                }
 
255
 
 
256
                SmartTagMarker currentSmartTag;
 
257
                DocumentLocation currentSmartTagBegin;
 
258
                void CreateSmartTag (List<CodeAction> fixes, DocumentLocation loc)
 
259
                {
 
260
                        Fixes = fixes;
 
261
                        if (!QuickTaskStrip.EnableFancyFeatures) {
 
262
                                RemoveWidget ();
 
263
                                return;
 
264
                        }
 
265
                        var editor = document.Editor;
 
266
                        if (editor == null || editor.Parent == null || !editor.Parent.IsRealized) {
 
267
                                RemoveWidget ();
 
268
                                return;
 
269
                        }
 
270
                        if (document.ParsedDocument == null || document.ParsedDocument.IsInvalid) {
 
271
                                RemoveWidget ();
 
272
                                return;
 
273
                        }
 
274
 
 
275
                        var container = editor.Parent;
 
276
                        if (container == null) {
 
277
                                RemoveWidget ();
 
278
                                return;
 
279
                        }
 
280
                        bool first = true;
 
281
                        DocumentLocation smartTagLocBegin = loc;
 
282
                        foreach (var fix in fixes) {
 
283
                                if (fix.DocumentRegion.IsEmpty)
 
284
                                        continue;
 
285
                                if (first || loc < fix.DocumentRegion.Begin) {
 
286
                                        smartTagLocBegin = fix.DocumentRegion.Begin;
 
287
                                }
 
288
                                first = false;
 
289
                        }
 
290
                        if (smartTagLocBegin.Line != loc.Line)
 
291
                                smartTagLocBegin = new DocumentLocation (loc.Line, 1);
 
292
                        // got no fix location -> try to search word start
 
293
                        if (first) {
 
294
                                int offset = document.Editor.LocationToOffset (smartTagLocBegin);
 
295
                                while (offset > 0) {
 
296
                                        char ch = document.Editor.GetCharAt (offset - 1);
 
297
                                        if (!char.IsLetterOrDigit (ch) && ch != '_')
 
298
                                                break;
 
299
                                        offset--;
 
300
                                }
 
301
                                smartTagLocBegin = document.Editor.OffsetToLocation (offset);
 
302
                        }
 
303
 
 
304
                        if (currentSmartTag != null && currentSmartTagBegin == smartTagLocBegin) {
 
305
                                currentSmartTag.fixes = fixes;
 
306
                                return;
 
307
                        }
 
308
                        currentSmartTagBegin = smartTagLocBegin;
 
309
 
 
310
                        RemoveWidget ();
 
311
                        var line = document.Editor.GetLine (smartTagLocBegin.Line);
 
312
                        currentSmartTag = new SmartTagMarker ((line.NextLine ?? line).Offset, this, fixes, smartTagLocBegin);
 
313
                        document.Editor.Document.AddMarker (currentSmartTag);
 
314
                }
146
315
                
147
316
                public override void Initialize ()
148
317
                {
149
318
                        base.Initialize ();
150
319
                        document.DocumentParsed += HandleDocumentDocumentParsed;
151
320
                        document.Editor.SelectionChanged += HandleSelectionChanged;
 
321
                        document.Editor.Parent.BeginHover += HandleBeginHover;
 
322
                }
 
323
 
 
324
                void HandleBeginHover (object sender, EventArgs e)
 
325
                {
 
326
                        CancelSmartTagPopupTimeout ();
 
327
                        CancelMenuCloseTimer ();
 
328
                }
 
329
 
 
330
                void StartMenuCloseTimer ()
 
331
                {
 
332
                        CancelMenuCloseTimer ();
 
333
                        menuCloseTimeoutId = GLib.Timeout.Add (menuTimeout, delegate {
 
334
                                if (codeActionMenu != null) {
 
335
                                        codeActionMenu.Destroy ();
 
336
                                        codeActionMenu = null;
 
337
                                }
 
338
                                menuCloseTimeoutId = 0;
 
339
                                return false;
 
340
                        });
152
341
                }
153
342
 
154
343
                void HandleSelectionChanged (object sender, EventArgs e)
164
353
                [CommandUpdateHandler(RefactoryCommands.QuickFix)]
165
354
                public void UpdateQuickFixCommand (CommandInfo ci)
166
355
                {
167
 
                        ci.Enabled = widget != null && widget.Visible;
 
356
                        if (QuickTaskStrip.EnableFancyFeatures) {
 
357
                                ci.Enabled = currentSmartTag != null;
 
358
                        } else {
 
359
                                ci.Enabled = true;
 
360
                        }
168
361
                }
169
362
                
170
363
                [CommandHandler(RefactoryCommands.QuickFix)]
171
364
                void OnQuickFixCommand ()
172
365
                {
173
 
                        if (widget == null || !widget.Visible)
174
 
                                return;
175
 
                        widget.PopupQuickFixMenu ();
 
366
                        if (!QuickTaskStrip.EnableFancyFeatures) {
 
367
                                var w = new CodeActionWidget (this, Document);
 
368
                                w.SetFixes (RefactoringService.GetValidActions (Document, Document.Editor.Caret.Location).Result, Document.Editor.Caret.Location);
 
369
                                w.PopupQuickFixMenu ();
 
370
                                w.Destroy ();
 
371
                                return;
 
372
                        }
 
373
                        if (currentSmartTag == null)
 
374
                                return;
 
375
                        currentSmartTag.Popup ();
 
376
                }
 
377
 
 
378
                internal List<CodeAction> GetCurrentFixes ()
 
379
                {
 
380
                        if (currentSmartTag == null)
 
381
                                return null;
 
382
                        return currentSmartTag.fixes;
176
383
                }
177
384
        }
178
385
}