You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
165 lines
7.3 KiB
165 lines
7.3 KiB
// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team |
|
// |
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this |
|
// software and associated documentation files (the "Software"), to deal in the Software |
|
// without restriction, including without limitation the rights to use, copy, modify, merge, |
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons |
|
// to whom the Software is furnished to do so, subject to the following conditions: |
|
// |
|
// The above copyright notice and this permission notice shall be included in all copies or |
|
// substantial portions of the Software. |
|
// |
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, |
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR |
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE |
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
|
// DEALINGS IN THE SOFTWARE. |
|
|
|
using System; |
|
using System.Collections.Generic; |
|
using System.IO; |
|
using System.Linq; |
|
using System.Threading; |
|
using ICSharpCode.AvalonEdit.Editing; |
|
using ICSharpCode.AvalonEdit.Snippets; |
|
using ICSharpCode.NRefactory.Editor; |
|
using ICSharpCode.SharpDevelop; |
|
using CSharpBinding.FormattingStrategy; |
|
using ICSharpCode.NRefactory.CSharp; |
|
using ICSharpCode.NRefactory.CSharp.Refactoring; |
|
using ICSharpCode.NRefactory.CSharp.Resolver; |
|
using ICSharpCode.NRefactory.CSharp.TypeSystem; |
|
using ICSharpCode.NRefactory.TypeSystem; |
|
using ICSharpCode.SharpDevelop.Editor; |
|
using ICSharpCode.SharpDevelop.Editor.CodeCompletion; |
|
using CSharpBinding.Refactoring; |
|
|
|
namespace CSharpBinding.Completion |
|
{ |
|
/// <summary> |
|
/// Item for 'override' completion of "ToString()" methods. |
|
/// </summary> |
|
class OverrideToStringCompletionData : OverrideCompletionData |
|
{ |
|
public OverrideToStringCompletionData(int declarationBegin, IMember m, CSharpResolver contextAtCaret) |
|
: base(declarationBegin, m, contextAtCaret) |
|
{ |
|
} |
|
|
|
public override void Complete(CompletionContext context) |
|
{ |
|
if (declarationBegin > context.StartOffset) { |
|
base.Complete(context); |
|
return; |
|
} |
|
|
|
TypeSystemAstBuilder b = new TypeSystemAstBuilder(contextAtCaret); |
|
b.ShowTypeParameterConstraints = false; |
|
b.GenerateBody = true; |
|
|
|
var entityDeclaration = b.ConvertEntity(this.Entity); |
|
entityDeclaration.Modifiers &= ~(Modifiers.Virtual | Modifiers.Abstract); |
|
entityDeclaration.Modifiers |= Modifiers.Override; |
|
|
|
var body = entityDeclaration.GetChildByRole(Roles.Body); |
|
Statement baseCallStatement = body.Children.OfType<Statement>().FirstOrDefault(); |
|
|
|
if (!this.Entity.IsAbstract) { |
|
// modify body to call the base method |
|
if (this.Entity.SymbolKind == SymbolKind.Method) { |
|
var baseCall = new BaseReferenceExpression().Invoke(this.Entity.Name, new Expression[] { }); |
|
if (((IMethod)this.Entity).ReturnType.IsKnownType(KnownTypeCode.Void)) |
|
baseCallStatement = new ExpressionStatement(baseCall); |
|
else |
|
baseCallStatement = new ReturnStatement(baseCall); |
|
|
|
// Clear body of inserted method |
|
entityDeclaration.GetChildByRole(Roles.Body).Statements.Clear(); |
|
} |
|
} |
|
|
|
var document = context.Editor.Document; |
|
StringWriter w = new StringWriter(); |
|
var formattingOptions = CSharpFormattingPolicies.Instance.GetProjectOptions(contextAtCaret.Compilation.GetProject()); |
|
var segmentDict = SegmentTrackingOutputFormatter.WriteNode( |
|
w, entityDeclaration, formattingOptions.OptionsContainer.GetEffectiveOptions(), context.Editor.Options); |
|
|
|
using (document.OpenUndoGroup()) { |
|
InsertionContext insertionContext = new InsertionContext(context.Editor.GetService(typeof(TextArea)) as TextArea, declarationBegin); |
|
insertionContext.InsertionPosition = context.Editor.Caret.Offset; |
|
|
|
string newText = w.ToString().TrimEnd(); |
|
document.Replace(declarationBegin, context.EndOffset - declarationBegin, newText); |
|
var throwStatement = entityDeclaration.Descendants.FirstOrDefault(n => n is ThrowStatement); |
|
if (throwStatement != null) { |
|
var segment = segmentDict[throwStatement]; |
|
context.Editor.Select(declarationBegin + segment.Offset, segment.Length); |
|
} |
|
CSharpFormatterHelper.Format(context.Editor, declarationBegin, newText.Length, formattingOptions.OptionsContainer); |
|
|
|
var refactoringContext = SDRefactoringContext.Create(context.Editor, CancellationToken.None); |
|
var typeResolveContext = refactoringContext.GetTypeResolveContext(); |
|
if (typeResolveContext == null) { |
|
return; |
|
} |
|
var resolvedCurrent = typeResolveContext.CurrentTypeDefinition; |
|
var entities = FindFieldsAndProperties(resolvedCurrent).ToList(); |
|
if (entities.Any()) { |
|
IEditorUIService uiService = context.Editor.GetService(typeof(IEditorUIService)) as IEditorUIService; |
|
|
|
ITextAnchor endAnchor = context.Editor.Document.CreateAnchor(context.Editor.Caret.Offset); |
|
endAnchor.MovementType = AnchorMovementType.AfterInsertion; |
|
|
|
ITextAnchor startAnchor = context.Editor.Document.CreateAnchor(context.Editor.Caret.Offset); |
|
startAnchor.MovementType = AnchorMovementType.BeforeInsertion; |
|
|
|
ITextAnchor insertionPos = context.Editor.Document.CreateAnchor(endAnchor.Offset); |
|
insertionPos.MovementType = AnchorMovementType.BeforeInsertion; |
|
|
|
AbstractInlineRefactorDialog dialog = new OverrideToStringMethodDialog(insertionContext, context.Editor, insertionPos, entities, baseCallStatement); |
|
dialog.Element = uiService.CreateInlineUIElement(insertionPos, dialog); |
|
|
|
insertionContext.RegisterActiveElement(new InlineRefactorSnippetElement(cxt => null, ""), dialog); |
|
} else { |
|
if (baseCallStatement != null) { |
|
// Add default base call |
|
MethodDeclaration insertedOverrideMethod = refactoringContext.GetNode().PrevSibling as MethodDeclaration; |
|
if (insertedOverrideMethod == null) |
|
{ |
|
// We are not inside of a method declaration |
|
return; |
|
} |
|
using (Script script = refactoringContext.StartScript()) { |
|
script.AddTo(insertedOverrideMethod.Body, baseCallStatement); |
|
} |
|
} |
|
} |
|
|
|
insertionContext.RaiseInsertionCompleted(EventArgs.Empty); |
|
} |
|
} |
|
|
|
IEnumerable<PropertyOrFieldWrapper> FindFieldsAndProperties(IType sourceType) |
|
{ |
|
int i = 0; |
|
|
|
foreach (var f in sourceType.GetFields().Where(field => !field.IsConst |
|
&& field.IsStatic == sourceType.GetDefinition().IsStatic |
|
&& field.DeclaringType.FullName == sourceType.FullName |
|
&& field.ReturnType != null)) { |
|
yield return new PropertyOrFieldWrapper(f) { Index = i }; |
|
i++; |
|
} |
|
|
|
foreach (var p in sourceType.GetProperties().Where(prop => prop.CanGet && !prop.IsIndexer |
|
&& prop.IsAutoImplemented() |
|
&& prop.IsStatic == sourceType.GetDefinition().IsStatic |
|
&& prop.DeclaringType.FullName == sourceType.FullName |
|
&& prop.ReturnType != null)) { |
|
yield return new PropertyOrFieldWrapper(p) { Index = i }; |
|
i++; |
|
} |
|
} |
|
} |
|
}
|
|
|