2
// MDRefactoringContext.cs
5
// Mike Krüger <mkrueger@novell.com>
7
// Copyright (c) 2011 Novell, Inc (http://www.novell.com)
9
// Permission is hereby granted, free of charge, to any person obtaining a copy
10
// of this software and associated documentation files (the "Software"), to deal
11
// in the Software without restriction, including without limitation the rights
12
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
// copies of the Software, and to permit persons to whom the Software is
14
// furnished to do so, subject to the following conditions:
16
// The above copyright notice and this permission notice shall be included in
17
// all copies or substantial portions of the Software.
19
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28
using MonoDevelop.CSharp.Resolver;
29
using ICSharpCode.NRefactory.CSharp;
30
using System.Collections.Generic;
31
using Mono.TextEditor;
32
using MonoDevelop.Projects;
33
using MonoDevelop.Core;
34
using MonoDevelop.Refactoring;
35
using ICSharpCode.NRefactory.CSharp.Refactoring;
36
using ICSharpCode.NRefactory.TypeSystem;
37
using ICSharpCode.NRefactory.TypeSystem.Implementation;
38
using ICSharpCode.NRefactory.CSharp.Resolver;
40
namespace MonoDevelop.CSharp.ContextAction
42
public class MDRefactoringContext : RefactoringContext
44
public MonoDevelop.Ide.Gui.Document Document {
49
public override bool HasCSharp3Support {
51
var project = Document.Project as DotNetProject;
54
switch (project.TargetFramework.ClrVersion) {
55
case ClrVersion.Net_1_1:
56
case ClrVersion.Net_2_0:
57
case ClrVersion.Clr_2_1:
65
SimpleProjectContent content;
66
public override ITypeResolveContext TypeResolveContext {
68
return new SimpleProjectContent ();
69
/* if (content == null) {
70
content = new SimpleProjectContent ();
71
var newTypes = new List<ITypeDefinition> ();
73
foreach (var file in Document.Project.Files) {
74
if (!string.Equals (file.BuildAction, "compile", StringComparison.OrdinalIgnoreCase))
76
var visitor = new TypeSystemConvertVisitor (content, file.FilePath);
77
CSharpParser parser = new CSharpParser ();
78
using (var stream = System.IO.File.OpenRead (file.FilePath)) {
79
var unit = parser.Parse (stream);
80
unit.AcceptVisitor (visitor, null);
82
Console.WriteLine (file.FilePath + ": add:" + visitor.ParsedFile.TopLevelTypeDefinitions.Count);
83
content.UpdateProjectContent (null, visitor.ParsedFile.TopLevelTypeDefinitions, null, null);
90
public override CSharpFormattingOptions FormattingOptions {
92
var dom = Document.Dom;
93
var policyParent = dom != null && dom.Project != null ? dom.Project.Policies : null;
94
var types = MonoDevelop.Ide.DesktopService.GetMimeTypeInheritanceChain (MonoDevelop.CSharp.Formatting.CSharpFormatter.MimeType);
95
var codePolicy = policyParent != null ? policyParent.Get<MonoDevelop.CSharp.Formatting.CSharpFormattingPolicy> (types) : MonoDevelop.Projects.Policies.PolicyService.GetDefaultPolicy<MonoDevelop.CSharp.Formatting.CSharpFormattingPolicy> (types);
96
return codePolicy.CreateOptions ();
100
public override string EolMarker {
102
return Document.Editor.EolMarker;
106
public override bool IsSomethingSelected {
108
return Document.Editor.IsSomethingSelected;
112
public override string SelectedText {
114
return Document.Editor.SelectedText;
118
public override int SelectionStart {
120
return Document.Editor.SelectionRange.Offset;
124
public override int SelectionEnd {
126
return Document.Editor.SelectionRange.EndOffset;
130
public override int SelectionLength {
132
return Document.Editor.SelectionRange.Length;
136
public override int GetOffset (AstLocation location)
138
return Document.Editor.LocationToOffset (location.Line, location.Column);
141
public override AstLocation GetLocation (int offset)
143
var loc = Document.Editor.OffsetToLocation (offset);
144
return new AstLocation (loc.Line, loc.Column);
147
public override string GetText (int offset, int length)
149
return Document.Editor.GetTextAt (offset, length);
152
#region IChangeFactory implementation
154
class MdTextReplaceAction : TextReplaceAction
156
MonoDevelop.Ide.Gui.Document doc;
158
public MdTextReplaceAction (MonoDevelop.Ide.Gui.Document doc, int offset, int removedChars, string insertedText) : base (offset, removedChars, insertedText)
161
throw new ArgumentNullException ("doc");
165
public override void Perform (Script script)
167
doc.Editor.Replace (Offset, RemovedChars, InsertedText);
171
public override TextReplaceAction CreateTextReplaceAction (int offset, int removedChars, string insertedText)
173
return new MdTextReplaceAction (Document, offset, removedChars, insertedText);
176
class MdNodeOutputAction : NodeOutputAction
178
MonoDevelop.Ide.Gui.Document doc;
180
public MdNodeOutputAction (MonoDevelop.Ide.Gui.Document doc, int offset, int removedChars, NodeOutput output) : base (offset, removedChars, output)
183
throw new ArgumentNullException ("doc");
185
throw new ArgumentNullException ("output");
189
public override void Perform (Script script)
191
doc.Editor.Replace (Offset, RemovedChars, NodeOutput.Text);
195
public override NodeOutputAction CreateNodeOutputAction (int offset, int removedChars, NodeOutput output)
197
return new MdNodeOutputAction (Document, offset, removedChars, output);
200
class MdNodeSelectionAction : NodeSelectionAction
202
MonoDevelop.Ide.Gui.Document doc;
204
public MdNodeSelectionAction (MonoDevelop.Ide.Gui.Document doc, AstNode node) : base (node)
207
throw new ArgumentNullException ("doc");
211
public override void Perform (Script script)
213
foreach (var action in script.Actions) {
216
var noa = action as NodeOutputAction;
219
NodeOutput.Segment segment;
220
if (noa.NodeOutput.NodeSegments.TryGetValue (AstNode, out segment)) {
221
var lead = noa.Offset + segment.EndOffset;
222
doc.Editor.Caret.Offset = lead;
223
doc.Editor.SetSelection (noa.Offset + segment.Offset, lead);
229
public override NodeSelectionAction CreateNodeSelectionAction (AstNode node)
231
return new MdNodeSelectionAction (this.Document, node);
234
class MdFormatTextAction : FormatTextAction
236
MDRefactoringContext ctx;
238
public MdFormatTextAction (MDRefactoringContext ctx, Func<RefactoringContext, AstNode> callback) : base (callback)
243
public override void Perform (Script script)
245
ctx.Document.UpdateParseDocument ();
246
ctx.Unit = ctx.Document.ParsedDocument.LanguageAST as ICSharpCode.NRefactory.CSharp.CompilationUnit;
248
var node = Callback (ctx);
250
node.FormatText (ctx.Document);
254
public override FormatTextAction CreateFormatTextAction (Func<RefactoringContext, AstNode> callback)
256
return new MdFormatTextAction (this, callback);
259
class MdLinkAction : CreateLinkAction
261
MDRefactoringContext ctx;
263
public MdLinkAction (MDRefactoringContext ctx, IEnumerable<AstNode> linkedNodes) : base (linkedNodes)
268
public override void Perform (Script script)
270
List<Segment> segments = new List<Segment> ();
271
foreach (var action in script.Actions) {
274
var noa = action as NodeOutputAction;
277
foreach (var astNode in Linked) {
278
NodeOutput.Segment segment;
279
if (noa.NodeOutput.NodeSegments.TryGetValue (astNode, out segment))
280
segments.Add (new Segment (noa.Offset + segment.Offset, segment.Length));
283
segments.Sort ((x, y) => x.Offset.CompareTo (y.Offset));
284
var link = new TextLink ("name");
285
segments.ForEach (s => link.AddLink (s));
286
var links = new List<TextLink> ();
288
var tle = new TextLinkEditMode (ctx.Document.Editor.Parent, 0, links);
289
tle.SetCaretPosition = false;
290
if (tle.ShouldStartTextLinkMode) {
291
tle.OldMode = ctx.Document.Editor.CurrentMode;
293
ctx.Document.Editor.CurrentMode = tle;
298
public override CreateLinkAction CreateLinkAction (IEnumerable<AstNode> linkedNodes)
300
return new MdLinkAction (this, linkedNodes);
305
public class MdScript : Script
307
MDRefactoringContext ctx;
309
public MdScript (MDRefactoringContext ctx) : base(ctx)
314
public static void RunActions (IList<ICSharpCode.NRefactory.CSharp.Refactoring.Action> actions, Script script)
316
for (int i = 0; i < actions.Count; i++) {
317
actions [i].Perform (script);
318
var replaceChange = actions [i] as TextReplaceAction;
319
if (replaceChange == null)
321
for (int j = 0; j < actions.Count; j++) {
324
var change = actions [j] as TextReplaceAction;
327
if (replaceChange.Offset >= 0 && change.Offset >= 0) {
328
if (replaceChange.Offset < change.Offset) {
329
change.Offset -= replaceChange.RemovedChars;
330
if (!string.IsNullOrEmpty (replaceChange.InsertedText))
331
change.Offset += replaceChange.InsertedText.Length;
332
} else if (replaceChange.Offset < change.Offset + change.RemovedChars) {
333
change.RemovedChars = Math.Max (0, change.RemovedChars - replaceChange.RemovedChars);
334
change.Offset = replaceChange.Offset + (!string.IsNullOrEmpty (replaceChange.InsertedText) ? replaceChange.InsertedText.Length : 0);
341
public override void Dispose ()
343
RunActions (changes, this);
346
public override void InsertWithCursor (string operation, AstNode node, InsertPosition defaultPosition)
348
var editor = ctx.Document.Editor;
349
var mode = new InsertionCursorEditMode (editor.Parent, MonoDevelop.Ide.CodeGenerationService.GetInsertionPoints (ctx.Document, ctx.Document.CompilationUnit.GetTypeAt (ctx.Location.Line, ctx.Location.Column)));
350
var helpWindow = new Mono.TextEditor.PopupWindow.ModeHelpWindow ();
351
helpWindow.TransientFor = MonoDevelop.Ide.IdeApp.Workbench.RootWindow;
352
helpWindow.TitleText = string.Format (GettextCatalog.GetString ("<b>{0} -- Targeting</b>"), operation);
353
helpWindow.Items.Add (new KeyValuePair<string, string> (GettextCatalog.GetString ("<b>Key</b>"), GettextCatalog.GetString ("<b>Behavior</b>")));
354
helpWindow.Items.Add (new KeyValuePair<string, string> (GettextCatalog.GetString ("<b>Up</b>"), GettextCatalog.GetString ("Move to <b>previous</b> target point.")));
355
helpWindow.Items.Add (new KeyValuePair<string, string> (GettextCatalog.GetString ("<b>Down</b>"), GettextCatalog.GetString ("Move to <b>next</b> target point.")));
356
helpWindow.Items.Add (new KeyValuePair<string, string> (GettextCatalog.GetString ("<b>Enter</b>"), GettextCatalog.GetString ("<b>Accept</b> target point.")));
357
helpWindow.Items.Add (new KeyValuePair<string, string> (GettextCatalog.GetString ("<b>Esc</b>"), GettextCatalog.GetString ("<b>Cancel</b> this operation.")));
358
mode.HelpWindow = helpWindow;
360
switch (defaultPosition) {
361
case InsertPosition.Start:
364
case InsertPosition.End:
365
mode.CurIndex = mode.InsertionPoints.Count - 1;
367
case InsertPosition.Before:
368
for (int i = 0; i < mode.InsertionPoints.Count; i++) {
369
if (mode.InsertionPoints [i].Location < new DocumentLocation (ctx.Location.Line, ctx.Location.Column))
373
case InsertPosition.After:
374
for (int i = 0; i < mode.InsertionPoints.Count; i++) {
375
if (mode.InsertionPoints [i].Location > new DocumentLocation (ctx.Location.Line, ctx.Location.Column)) {
384
mode.Exited += delegate(object s, InsertionCursorEventArgs iCArgs) {
385
if (iCArgs.Success) {
386
var output = OutputNode (GetIndentLevelAt (editor.LocationToOffset (iCArgs.InsertionPoint.Location)), node);
387
iCArgs.InsertionPoint.Insert (editor, output.Text);
394
public override Script StartScript ()
396
return new MdScript (this);
399
public MDRefactoringContext (MonoDevelop.Ide.Gui.Document document, MonoDevelop.Projects.Dom.DomLocation loc)
401
if (document == null)
402
throw new ArgumentNullException ("document");
403
this.Document = document;
404
this.Location = new AstLocation (loc.Line, loc.Column);
405
this.Unit = document.ParsedDocument.LanguageAST as ICSharpCode.NRefactory.CSharp.CompilationUnit;
408
/* public override AstType CreateShortType (AstType fullType)
410
return MonoDevelop.ContextAction.ContextAction.ShortenTypeName (Document, fullType.ConvertToReturnType ());
413
public override AstType CreateShortType (string fullTypeName)
415
return MonoDevelop.ContextAction.ContextAction.ShortenTypeName (Document, fullTypeName);
419
NRefactoryResolver resolver;
420
public NRefactoryResolver Resolver {
422
if (resolver == null)
423
resolver = new NRefactoryResolver (Document.Dom, Document.CompilationUnit, Document.Editor, Document.FileName);
428
Dictionary<AstNode, MonoDevelop.Projects.Dom.ResolveResult> resolveCache = new Dictionary<AstNode, MonoDevelop.Projects.Dom.ResolveResult> ();
429
MonoDevelop.Projects.Dom.ResolveResult Resolve (AstNode node)
431
MonoDevelop.Projects.Dom.ResolveResult result;
432
if (!resolveCache.TryGetValue (node, out result))
433
resolveCache [node] = result = Resolver.Resolve (node.ToString (), new MonoDevelop.Projects.Dom.DomLocation (Location.Line, Location.Column));
437
public override AstType ResolveType (AstNode node)
439
var resolveResult = Resolve (node);
440
if (resolveResult == null || resolveResult.ResolvedType == null || string.IsNullOrEmpty (resolveResult.ResolvedType.FullName))
442
return MonoDevelop.ContextAction.ContextAction.ShortenTypeName (Document, resolveResult.ResolvedType);
446
ParsedFile ParsedFile {
448
var visitor = new TypeSystemConvertVisitor ((IProjectContent) TypeResolveContext, Document.FileName);
449
Unit.AcceptVisitor (visitor, null);
450
return visitor.ParsedFile;
454
/* public override IEnumerable<ICSharpCode.NRefactory.TypeSystem.IMember> ResolveMember (Expression expression)
457
var csResolver = new CSharpResolver (TypeResolveContext, System.Threading.CancellationToken.None);
458
var navigator = new NodeListResolveVisitorNavigator (new[] { expression });
460
var visitor = new ICSharpCode.NRefactory.CSharp.Resolver.ResolveVisitor (csResolver, pf, navigator);
462
visitor.VisitCompilationUnit (Unit, null);
463
var resolveResult = visitor.Resolve (expression);
464
if (resolveResult == null)
466
if (resolveResult is MemberResolveResult) {
467
yield return ((MemberResolveResult)resolveResult).Member;
468
} else if (resolveResult is MethodGroupResolveResult) {
469
var mgg = (MethodGroupResolveResult)resolveResult;
470
foreach (var m in mgg.Methods)
475
public override void ReplaceReferences (ICSharpCode.NRefactory.TypeSystem.IMember member, MemberDeclaration replaceWidth)
480
// public override ICSharpCode.NRefactory.TypeSystem.ITypeDefinition GetDefinition (AstType resolvedType)
482
// var rr = Resolve (resolvedType);
485
// var type = Document.Dom.GetType (rr.ResolvedType);
488
// return TypeResolveContext.GetClass (type.Namespace, type.Name, type.TypeParameters.Count, StringComparer.InvariantCulture);
492
public bool IsValid {
498
public int GetIndentLevel (AstNode node)
500
return GetIndentLevel (node.StartLocation.Line);
503
public int GetIndentLevel (int line)
505
return Document.CalcIndentLevel (Document.Editor.GetLineIndent (line));
508
public void SetSelection (AstNode node)
510
this.selectNode = node;
513
AstNode selectNode = null;
516
NodeOutput OutputNode (AstNode node, int indentLevel, Action<int, AstNode> outputStarted = null)
518
NodeOutput result = new NodeOutput ();
520
return Document.OutputNode (node, indentLevel, delegate(int outOffset, AstNode outNode) {
521
result.nodeSegments [outNode] = new Segment (outOffset, 0);
522
if (outputStarted != null)
523
outputStarted (outOffset, outNode);
524
}, delegate(int outOffset, AstNode outNode) {
525
result.nodeSegments [outNode].Length = outOffset - result.nodeSegments [outNode].Offset;
529
public void StartTextLinkMode (int baseOffset, int replaceLength, IEnumerable<int> offsets)
536
public string GetText (AstNode node)
538
return Document.Editor.GetTextAt (GetSegment (node));
541
public ISegment GetSegment (AstNode node)
543
var startOffset = Document.Editor.LocationToOffset (node.StartLocation.Line, node.StartLocation.Column);
544
var endOffset = Document.Editor.LocationToOffset (node.EndLocation.Line, node.EndLocation.Column);
546
return new Segment (startOffset, endOffset - startOffset);
549
public void FormatText (Func<MDRefactoringContext, AstNode> update)
559
//these methods don't get used in 2.8
561
public override AstType CreateShortType (AstType fullType)
563
throw new NotImplementedException ();
566
public override AstType CreateShortType (ICSharpCode.NRefactory.TypeSystem.IType fullType)
568
throw new NotImplementedException ();
571
public override ResolveResult Resolve (AstNode expression)
573
throw new NotImplementedException ();