41
44
CodeActionWidget widget;
42
45
uint quickFixTimeout;
47
const int menuTimeout = 250;
48
uint smartTagPopupTimeoutId;
49
uint menuCloseTimeoutId;
44
52
public IEnumerable<CodeAction> Fixes {
57
void CancelSmartTagPopupTimeout ()
59
if (smartTagPopupTimeoutId != 0) {
60
GLib.Source.Remove (smartTagPopupTimeoutId);
61
smartTagPopupTimeoutId = 0;
65
void CancelMenuCloseTimer ()
67
if (menuCloseTimeoutId != 0) {
68
GLib.Source.Remove (menuCloseTimeoutId);
69
menuCloseTimeoutId = 0;
49
73
void RemoveWidget ()
79
if (currentSmartTag != null) {
80
document.Editor.Document.RemoveMarker (currentSmartTag);
81
currentSmartTag = null;
82
currentSmartTagBegin = DocumentLocation.Empty;
84
CancelSmartTagPopupTimeout ();
56
88
public override void Dispose ()
90
CancelMenuCloseTimer ();
58
91
CancelQuickFixTimer ();
59
92
document.Editor.SelectionChanged -= HandleSelectionChanged;
60
93
document.DocumentParsed -= HandleDocumentDocumentParsed;
94
document.Editor.Parent.BeginHover -= HandleBeginHover;
68
99
void CreateWidget (IEnumerable<CodeAction> fixes, TextLocation loc)
71
if (!QuickTaskStrip.EnableFancyFeatures) {
75
102
var editor = document.Editor;
76
if (editor == null || editor.Parent == null || !editor.Parent.IsRealized) {
80
if (document.ParsedDocument == null || document.ParsedDocument.IsInvalid) {
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 ()) {
98
var container = editor.Parent.Parent as TextEditorContainer;
99
if (container == null) {
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
115
if (!widget.Visible)
111
container.MoveTopLevelWidget (widget,
112
2 + (int)editor.Parent.TextViewMargin.XOffset,
113
-2 + (int)editor.Parent.LineToY (document.Editor.Caret.Line));
117
container.MoveTopLevelWidget (
115
123
widget.SetFixes (fixes, loc);
118
126
public void CancelQuickFixTimer ()
128
if (quickFixCancellationTokenSource != null)
129
quickFixCancellationTokenSource.Cancel ();
120
130
if (quickFixTimeout != 0) {
121
131
GLib.Source.Remove (quickFixTimeout);
122
132
quickFixTimeout = 0;
136
CancellationTokenSource quickFixCancellationTokenSource;
126
138
public override void CursorPositionChanged ()
128
140
CancelQuickFixTimer ();
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) {
148
ICSharpCode.NRefactory.Semantics.ResolveResult resolveResult;
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 (); });
158
if (currentSmartTag != null)
159
Application.Invoke (delegate { RemoveWidget (); });
134
163
Application.Invoke (delegate {
135
CreateWidget (fixes, loc);
164
if (token.IsCancellationRequested)
166
CreateSmartTag (fixes, loc);
136
167
quickFixTimeout = 0;
144
175
base.CursorPositionChanged ();
178
class SmartTagMarker : TextSegmentMarker, IActionTextLineMarker
180
CodeActionEditorExtension codeActionEditorExtension;
181
internal List<CodeAction> fixes;
182
DocumentLocation loc;
184
public SmartTagMarker (int offset, CodeActionEditorExtension codeActionEditorExtension, List<CodeAction> fixes, DocumentLocation loc) : base (offset, 0)
186
this.codeActionEditorExtension = codeActionEditorExtension;
191
public SmartTagMarker (int offset) : base (offset, 0)
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)
198
var line = editor.GetLine (loc.Line);
199
var x = editor.ColumnToX (line, loc.Column) - editor.HAdjustment.Value + editor.TextViewMargin.XOffset + editor.TextViewMargin.TextStartPosition;
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);
203
if (HslColor.Brightness (editor.ColorStyle.PlainText.Background) < 0.5) {
204
cr.Color = new Cairo.Color (0.8, 0.8, 1, 0.9);
206
cr.Color = new Cairo.Color (0.2, 0.2, 1, 0.9);
211
#region IActionTextLineMarker implementation
213
bool IActionTextLineMarker.MousePressed (TextEditor editor, MarginMouseEventArgs args)
218
void IActionTextLineMarker.MouseHover (TextEditor editor, MarginMouseEventArgs args, TextLineMarkerHoverResult result)
220
if (args.Button != 0)
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) {
230
codeActionEditorExtension.CancelSmartTagPopupTimeout ();
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 ();
244
codeActionEditorExtension.CancelMenuCloseTimer ();
248
codeActionEditorExtension.widget.Destroy ();
249
codeActionEditorExtension.smartTagPopupTimeoutId = 0;
256
SmartTagMarker currentSmartTag;
257
DocumentLocation currentSmartTagBegin;
258
void CreateSmartTag (List<CodeAction> fixes, DocumentLocation loc)
261
if (!QuickTaskStrip.EnableFancyFeatures) {
265
var editor = document.Editor;
266
if (editor == null || editor.Parent == null || !editor.Parent.IsRealized) {
270
if (document.ParsedDocument == null || document.ParsedDocument.IsInvalid) {
275
var container = editor.Parent;
276
if (container == null) {
281
DocumentLocation smartTagLocBegin = loc;
282
foreach (var fix in fixes) {
283
if (fix.DocumentRegion.IsEmpty)
285
if (first || loc < fix.DocumentRegion.Begin) {
286
smartTagLocBegin = fix.DocumentRegion.Begin;
290
if (smartTagLocBegin.Line != loc.Line)
291
smartTagLocBegin = new DocumentLocation (loc.Line, 1);
292
// got no fix location -> try to search word start
294
int offset = document.Editor.LocationToOffset (smartTagLocBegin);
296
char ch = document.Editor.GetCharAt (offset - 1);
297
if (!char.IsLetterOrDigit (ch) && ch != '_')
301
smartTagLocBegin = document.Editor.OffsetToLocation (offset);
304
if (currentSmartTag != null && currentSmartTagBegin == smartTagLocBegin) {
305
currentSmartTag.fixes = fixes;
308
currentSmartTagBegin = smartTagLocBegin;
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);
147
316
public override void Initialize ()
149
318
base.Initialize ();
150
319
document.DocumentParsed += HandleDocumentDocumentParsed;
151
320
document.Editor.SelectionChanged += HandleSelectionChanged;
321
document.Editor.Parent.BeginHover += HandleBeginHover;
324
void HandleBeginHover (object sender, EventArgs e)
326
CancelSmartTagPopupTimeout ();
327
CancelMenuCloseTimer ();
330
void StartMenuCloseTimer ()
332
CancelMenuCloseTimer ();
333
menuCloseTimeoutId = GLib.Timeout.Add (menuTimeout, delegate {
334
if (codeActionMenu != null) {
335
codeActionMenu.Destroy ();
336
codeActionMenu = null;
338
menuCloseTimeoutId = 0;
154
343
void HandleSelectionChanged (object sender, EventArgs e)
164
353
[CommandUpdateHandler(RefactoryCommands.QuickFix)]
165
354
public void UpdateQuickFixCommand (CommandInfo ci)
167
ci.Enabled = widget != null && widget.Visible;
356
if (QuickTaskStrip.EnableFancyFeatures) {
357
ci.Enabled = currentSmartTag != null;
170
363
[CommandHandler(RefactoryCommands.QuickFix)]
171
364
void OnQuickFixCommand ()
173
if (widget == null || !widget.Visible)
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 ();
373
if (currentSmartTag == null)
375
currentSmartTag.Popup ();
378
internal List<CodeAction> GetCurrentFixes ()
380
if (currentSmartTag == null)
382
return currentSmartTag.fixes;