Browse Source

trying to implement RenameContextAction for C#

pull/45/merge
Siegfried Pammer 12 years ago
parent
commit
9f5c8720a8
  1. 1
      src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.addin
  2. 2
      src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj
  3. 49
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpLanguageBinding.cs
  4. 41
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpTextEditorExtension.cs
  5. 221
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CSharpCodeGenerator.cs
  6. 181
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/RenameContextAction.cs
  7. 117
      src/Main/Base/Project/Refactoring/ICodeGenerator.cs
  8. 8
      src/Main/Base/Project/Src/Services/LanguageBinding/DefaultLanguageBinding.cs
  9. 14
      src/Main/Base/Project/Src/Services/LanguageBinding/ILanguageBinding.cs
  10. 77
      src/Main/Base/Project/Src/Services/RefactoringService/FindReferenceService.cs

1
src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.addin

@ -309,5 +309,6 @@ @@ -309,5 +309,6 @@
<CSharpCodeActionProvider class = "ICSharpCode.NRefactory.CSharp.Refactoring.UseStringFormatAction" />
<CSharpCodeActionProvider class = "ICSharpCode.NRefactory.CSharp.Refactoring.UseVarKeywordAction" />
<Class class="CSharpBinding.Refactoring.MoveTypeToFileContextAction" />
<Class class="CSharpBinding.Refactoring.RenameContextAction" />
</Path>
</AddIn>

2
src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj

@ -72,6 +72,7 @@ @@ -72,6 +72,7 @@
<Compile Include="Src\Completion\ImportCompletionData.cs" />
<Compile Include="Src\Completion\OverrideCompletionData.cs" />
<Compile Include="Src\Completion\SegmentTrackingOutputFormatter.cs" />
<Compile Include="Src\CSharpTextEditorExtension.cs" />
<Compile Include="Src\FormattingStrategy\CSharpFormatter.cs" />
<Compile Include="Src\Parser\FoldingVisitor.cs" />
<Compile Include="Src\Refactoring\CSharpCodeGenerator.cs" />
@ -85,6 +86,7 @@ @@ -85,6 +86,7 @@
<Compile Include="Src\Refactoring\IssueManager.cs" />
<Compile Include="Src\Refactoring\IssueOptionsViewModel.cs" />
<Compile Include="Src\Refactoring\MoveTypeToFileContextAction.cs" />
<Compile Include="Src\Refactoring\RenameContextAction.cs" />
<Compile Include="Src\Refactoring\SDNamingConventionService.cs" />
<Compile Include="Src\Refactoring\SDRedundantUsingIssue.cs" />
<Compile Include="Src\Refactoring\EditorScript.cs" />

49
src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpLanguageBinding.cs

@ -2,17 +2,12 @@ @@ -2,17 +2,12 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using CSharpBinding.FormattingStrategy;
using CSharpBinding.Refactoring;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Refactoring;
using CSharpBinding.FormattingStrategy;
using CSharpBinding.Refactoring;
namespace CSharpBinding
{
@ -21,6 +16,14 @@ namespace CSharpBinding @@ -21,6 +16,14 @@ namespace CSharpBinding
/// </summary>
public class CSharpLanguageBinding : DefaultLanguageBinding
{
public override string Name {
get { return "C#"; }
}
public override StringComparer IdentifierComparer {
get { return StringComparer.Ordinal; }
}
public override IFormattingStrategy FormattingStrategy {
get { return new CSharpFormattingStrategy(); }
}
@ -33,36 +36,4 @@ namespace CSharpBinding @@ -33,36 +36,4 @@ namespace CSharpBinding
get { return new CSharpCodeGenerator(); }
}
}
public class CSharpTextEditorExtension : ITextEditorExtension
{
ITextEditor editor;
IssueManager inspectionManager;
IList<IContextActionProvider> contextActionProviders;
public void Attach(ITextEditor editor)
{
this.editor = editor;
inspectionManager = new IssueManager(editor);
//codeManipulation = new CodeManipulation(editor);
if (!editor.ContextActionProviders.IsReadOnly) {
contextActionProviders = AddInTree.BuildItems<IContextActionProvider>("/SharpDevelop/ViewContent/TextEditor/C#/ContextActions", null);
editor.ContextActionProviders.AddRange(contextActionProviders);
}
}
public void Detach()
{
//codeManipulation.Dispose();
if (inspectionManager != null) {
inspectionManager.Dispose();
inspectionManager = null;
}
if (contextActionProviders != null) {
editor.ContextActionProviders.RemoveAll(contextActionProviders.Contains);
}
this.editor = null;
}
}
}

41
src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpTextEditorExtension.cs

@ -0,0 +1,41 @@ @@ -0,0 +1,41 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)using System;
using System.Collections.Generic;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Refactoring;
using CSharpBinding.Refactoring;
namespace CSharpBinding
{
public class CSharpTextEditorExtension : ITextEditorExtension
{
ITextEditor editor;
IssueManager inspectionManager;
IList<IContextActionProvider> contextActionProviders;
public void Attach(ITextEditor editor)
{
this.editor = editor;
inspectionManager = new IssueManager(editor);
//codeManipulation = new CodeManipulation(editor);
if (!editor.ContextActionProviders.IsReadOnly) {
contextActionProviders = AddInTree.BuildItems<IContextActionProvider>("/SharpDevelop/ViewContent/TextEditor/C#/ContextActions", null);
editor.ContextActionProviders.AddRange(contextActionProviders);
}
}
public void Detach()
{
//codeManipulation.Dispose();
if (inspectionManager != null) {
inspectionManager.Dispose();
inspectionManager = null;
}
if (contextActionProviders != null) {
editor.ContextActionProviders.RemoveAll(contextActionProviders.Contains);
}
this.editor = null;
}
}
}

221
src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CSharpCodeGenerator.cs

@ -2,26 +2,27 @@ @@ -2,26 +2,27 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Media.Animation;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.Core;
using ICSharpCode.Core.Presentation;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.CSharp.Refactoring;
using ICSharpCode.NRefactory.CSharp.Resolver;
using ICSharpCode.NRefactory.CSharp.TypeSystem;
using ICSharpCode.NRefactory.Editor;
using ICSharpCode.NRefactory.Semantics;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Project;
using ICSharpCode.SharpDevelop.Refactoring;
using CSharpBinding.Parser;
namespace CSharpBinding.Refactoring
{
@ -30,12 +31,12 @@ namespace CSharpBinding.Refactoring @@ -30,12 +31,12 @@ namespace CSharpBinding.Refactoring
/// </summary>
public class CSharpCodeGenerator : DefaultCodeGenerator
{
public void AddAttribute(IEntity target, IAttribute attribute)
public override void AddAttribute(IEntity target, IAttribute attribute)
{
AddAttribute(target.Region, attribute);
}
public void AddAssemblyAttribute(IProject targetProject, IAttribute attribute)
public override void AddAssemblyAttribute(IProject targetProject, IAttribute attribute)
{
// FIXME : will fail if there are no assembly attributes
ICompilation compilation = SD.ParserService.GetCompilation(targetProject);
@ -45,12 +46,12 @@ namespace CSharpBinding.Refactoring @@ -45,12 +46,12 @@ namespace CSharpBinding.Refactoring
AddAttribute(target.Region, attribute, "assembly");
}
public void AddReturnTypeAttribute(IMethod target, IAttribute attribute)
public override void AddReturnTypeAttribute(IMethod target, IAttribute attribute)
{
AddAttribute(target.Region, attribute, "return");
}
public void InsertEventHandler(ITypeDefinition target, string name, IEvent eventDefinition, bool jumpTo)
public override void InsertEventHandler(ITypeDefinition target, string name, IEvent eventDefinition, bool jumpTo)
{
IUnresolvedTypeDefinition match = null;
@ -110,5 +111,209 @@ namespace CSharpBinding.Refactoring @@ -110,5 +111,209 @@ namespace CSharpBinding.Refactoring
script.InsertBefore(node, attr);
}
}
public override bool IsValidIdentifier(string identifier)
{
if (string.IsNullOrWhiteSpace(identifier))
return false;
if (char.IsDigit(identifier.First()))
return false;
return identifier.All(IsSupportedCharacter);
}
bool IsSupportedCharacter(char ch)
{
if (ch == '_') return true;
switch (char.GetUnicodeCategory(ch)) {
case UnicodeCategory.UppercaseLetter:
case UnicodeCategory.LowercaseLetter:
case UnicodeCategory.TitlecaseLetter:
case UnicodeCategory.ModifierLetter:
case UnicodeCategory.OtherLetter:
case UnicodeCategory.NonSpacingMark:
case UnicodeCategory.SpacingCombiningMark:
case UnicodeCategory.DecimalDigitNumber:
case UnicodeCategory.LetterNumber:
case UnicodeCategory.ConnectorPunctuation:
case UnicodeCategory.Format:
return true;
default:
return false;
}
}
public override string EscapeIdentifier(string identifier)
{
if (!IsValidIdentifier(identifier))
throw new ArgumentException("Cannot escape this identifier!");
identifier = EscapeIdentifierInternal(identifier);
if (IsKeyword(identifier))
return "@" + identifier;
return identifier;
}
string EscapeIdentifierInternal(string identifier)
{
return string.Concat(identifier.Select(EscapeCharacter));
}
string EscapeCharacter(char ch)
{
if (char.IsLetterOrDigit(ch))
return ch.ToString();
return string.Format("\\x{0:000X}", ch);
}
#region Keywords
static readonly string[] keywords = new string [] {
"abstract", "as", "base", "bool", "break",
"byte", "case", "catch", "char", "checked",
"class", "const", "continue", "decimal", "default",
"delegate", "do", "double", "else", "enum",
"event", "explicit", "extern", "false", "finally",
"fixed", "float", "for", "foreach", "goto",
"if", "implicit", "in", "int", "interface",
"internal", "is", "lock", "long", "namespace",
"new", "null", "object", "operator", "out",
"override", "params", "private", "protected", "public",
"readonly", "ref", "return", "sbyte", "sealed",
"short", "sizeof", "stackalloc", "static", "string",
"struct", "switch", "this", "throw", "true",
"try", "typeof", "uint", "ulong", "unchecked",
"unsafe", "ushort", "using", "virtual", "void",
"volatile", "while"
};
static readonly string[] contextualKeywords = new string[] {
"add", "async", "await", "get", "partial",
"remove", "set", "where", "yield", "from",
"select", "group", "into", "orderby", "join",
"let", "on", "equals", "by", "ascending",
"descending", "dynamic", "var", "global"
};
public override bool IsKeyword(string identifier, bool treatContextualKeywordsAsIdentifiers = true)
{
if (contextualKeywords.Any(keyword => keyword.Equals(identifier, StringComparison.Ordinal)))
return !treatContextualKeywordsAsIdentifiers;
if (keywords.Any(keyword => keyword.Equals(identifier, StringComparison.Ordinal)))
return true;
return false;
}
#endregion
public override void RenameSymbol(RenameReferenceContext context, string newName)
{
newName = EscapeIdentifier(newName);
if (context.HasConflicts) throw new InvalidOperationException();
ITextEditorProvider provider = SD.FileService.OpenFile(context.RenameTarget.FileName) as ITextEditorProvider;
if (provider == null) return;
var editor = provider.TextEditor;
int offset = context.RenameTarget.StartOffset;
int length = context.RenameTarget.Length;
editor.Document.Replace(offset, length, newName);
}
enum ConflictResolution { RequireThis, RequireFullName, NoResolution }
public override IEnumerable<Conflict> FindRenamingConflicts(Reference context, string newName)
{
newName = EscapeIdentifier(newName);
ICompilation compilation = SD.ParserService.GetCompilationForFile(context.FileName);
ITextSource fileContent = new ParseableFileContentFinder().Create(context.FileName);
CSharpFullParseInformation pi = SD.ParserService.Parse(context.FileName, fileContent) as CSharpFullParseInformation;
if (pi == null) yield break;
AstNode node = pi.SyntaxTree.GetNodeContaining(context.StartLocation, context.EndLocation);
while (node.Parent is Expression)
node = node.Parent;
node = node.Clone();
var identifierNode = FindIdentifier(node, context.StartLocation, context.EndLocation);
node = SetName(identifierNode, newName);
var writer = new StringWriter();
var visitor = new CSharpOutputVisitor(writer, FormattingOptionsFactory.CreateSharpDevelop());
node.AcceptVisitor(visitor);
string expression = writer.ToString();
ResolveResult rr = SD.ParserService.ResolveSnippet(context.FileName, context.StartLocation, fileContent,
expression, compilation, default(CancellationToken));
if (!rr.IsError) {
if (rr is LocalResolveResult) {
IVariable culprit = ((LocalResolveResult)rr).Variable;
// if (rr.GetType() == context.ResolveResult.GetType())
yield return new CSharpConflict(context, culprit, ConflictResolution.NoResolution);
// else
// yield return new CSharpConflict(context, culprit, ConflictResolution.RequireThis);
} else if (rr is MemberResolveResult) {
IMember culprit = ((MemberResolveResult)rr).Member;
// if (rr.GetType() == context.ResolveResult.GetType())
yield return new CSharpConflict(context, culprit, ConflictResolution.NoResolution);
// else
// yield return new CSharpConflict(context, culprit, ConflictResolution.RequireThis);
} else if (rr is TypeResolveResult) {
IEntity culprit = rr.Type.GetDefinition();
// if (rr.GetType() == context.ResolveResult.GetType())
yield return new CSharpConflict(context, culprit, ConflictResolution.NoResolution);
// else
// yield return new CSharpConflict(context, culprit, ConflictResolution.RequireThis);
} else {
yield return new UnknownConflict();
}
}
}
Identifier FindIdentifier(AstNode node, TextLocation startLocation, TextLocation endLocation)
{
var id = node.GetAdjacentNodeAt(startLocation, n => n is Identifier) as Identifier;
return id;
}
AstNode SetName(AstNode old, string newName)
{
((Identifier)old).Name = newName;
return old;
}
class CSharpConflict : Conflict
{
ConflictResolution resolution;
Reference target;
public CSharpConflict(Reference target, IVariable variable, ConflictResolution resolution)
: base(variable)
{
this.target = target;
this.resolution = resolution;
}
public CSharpConflict(Reference target, IEntity entity, ConflictResolution resolution)
: base(entity)
{
this.target = target;
this.resolution = resolution;
}
public override void Solve()
{
switch (resolution) {
case ConflictResolution.RequireThis:
break;
case ConflictResolution.RequireFullName:
break;
case ConflictResolution.NoResolution:
throw new InvalidOperationException();
default:
throw new ArgumentOutOfRangeException();
}
}
public override bool IsSolvableConflict {
get { return resolution != ConflictResolution.NoResolution; }
}
}
}
}

181
src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/RenameContextAction.cs

@ -0,0 +1,181 @@ @@ -0,0 +1,181 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.Core;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.CSharp.Refactoring;
using ICSharpCode.NRefactory.Editor;
using ICSharpCode.NRefactory.Semantics;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Editor.Search;
using ICSharpCode.SharpDevelop.Parser;
using ICSharpCode.SharpDevelop.Project;
using ICSharpCode.SharpDevelop.Refactoring;
using CSharpBinding.Parser;
namespace CSharpBinding.Refactoring
{
[ContextAction("Rename", Description = "Renames a symbol and all references to it.")]
public class RenameContextAction : ContextAction
{
public override async Task<bool> IsAvailableAsync(EditorRefactoringContext context, CancellationToken cancellationToken)
{
var rr = await context.GetCurrentSymbolAsync();
if (rr == null || rr.GetDefinitionRegion().IsEmpty) return false;
if (rr is MemberResolveResult) {
// check whether all members in the hierarchy are defined in code...
IEntity thisMember = GetSurrogateEntity(((MemberResolveResult)rr).Member);
switch (thisMember.EntityType) {
case EntityType.TypeDefinition:
case EntityType.Field:
return !thisMember.Region.IsEmpty;
break;
case EntityType.Property:
case EntityType.Event:
case EntityType.Method:
return !FindReferenceService.FindAllMembers((IMember)thisMember)
.Distinct()
.Any(m => m.Region.IsEmpty);
case EntityType.Indexer:
return false;
case EntityType.Operator:
return false;
default:
throw new NotSupportedException();
}
}
return true;
}
IEntity GetSurrogateEntity(IMember member)
{
if (member.EntityType == EntityType.Accessor)
return ((IMethod)member).AccessorOwner;
if (member.EntityType == EntityType.Constructor || member.EntityType == EntityType.Destructor)
return member.DeclaringTypeDefinition;
return member;
}
public override async void Execute(EditorRefactoringContext context)
{
var rr = await context.GetCurrentSymbolAsync();
List<SearchedFile> references = null;
string oldName = null;
string text = null;
bool isLocal = rr is LocalResolveResult;
IVariable variable = null;
IEntity entity = null;
IProgressMonitor monitor = new DummyProgressMonitor();
if (isLocal) {
variable = ((LocalResolveResult)rr).Variable;
oldName = variable.Name;
text = "${res:SharpDevelop.Refactoring.RenameMemberText}";
references = await FindReferenceService.FindLocalReferences(variable, monitor).ToListAsync();
} else if (rr is TypeResolveResult) {
ITypeDefinition td = rr.Type.GetDefinition();
if (td == null) return;
entity = td;
oldName = td.Name;
text = "${res:SharpDevelop.Refactoring.RenameClassText}";
references = await FindReferenceService.FindReferences(td, monitor).ToListAsync();
} else if (rr is MemberResolveResult) {
entity = GetSurrogateEntity(((MemberResolveResult)rr).Member);
oldName = entity.Name;
if (entity is IMember) {
text = "${res:SharpDevelop.Refactoring.RenameMemberText}";
references = await FindReferenceService.FindReferencesToHierarchy((IMember)entity, monitor).ToListAsync();
} else {
text = "${res:SharpDevelop.Refactoring.RenameClassText}";
references = await FindReferenceService.FindReferences(entity, monitor).ToListAsync();
}
}
if (references == null || oldName == null) return;
string newName = SD.MessageService.ShowInputBox("${res:SharpDevelop.Refactoring.Rename}", text, oldName);
if (newName == null) return;
// if both identifiers are the same (in the language the symbol was defined in)
// => abort rename
ILanguageBinding definitionLanguage = SD.LanguageService
.GetLanguageByFileName(new FileName(rr.GetDefinitionRegion().FileName));
if (definitionLanguage.IdentifierComparer.Compare(oldName, newName) == 0)
return;
// check if identifier is valid in all target languages:
string lastExtension = null;
ILanguageBinding language = null;
bool isRenamePossible = true;
Dictionary<Reference, Conflict[]> conflicts = new Dictionary<Reference, Conflict[]>();
foreach (var file in references) {
string ext = file.FileName.GetExtension();
if (language == null || !string.Equals(ext, lastExtension, StringComparison.OrdinalIgnoreCase)) {
lastExtension = ext;
language = SD.LanguageService.GetLanguageByExtension(ext);
}
if (!language.CodeGenerator.IsValidIdentifier(newName)) {
isRenamePossible = false;
SD.MessageService.ShowErrorFormatted("The symbol '{0}' cannot be renamed, because its new name '{1}' would be invalid in {2}, please choose a different name and try again.", oldName, newName, language.Name);
break;
}
foreach (Reference reference in file.Matches.OfType<Reference>()) {
var currentConflicts = language.CodeGenerator.FindRenamingConflicts(reference, newName).ToArray();
if (currentConflicts.Any(conflict => !conflict.IsSolvableConflict)) {
isRenamePossible = false;
SD.MessageService.ShowErrorFormatted("The symbol '{0}' cannot be renamed, because its new name '{1}' would cause an unsolvable conflict in {2}, at line {3}, column {4}, please choose a different name and try again.", oldName, newName, reference.FileName, reference.StartLocation.Line, reference.StartLocation.Column);
break;
}
conflicts.Add(reference, currentConflicts);
}
}
// TODO : ask if user wants to rename corresponding IField/IProperty/IEvent as well...
if (!isRenamePossible) return;
lastExtension = null;
language = null;
foreach (var reference in references.SelectMany(file => file.Matches).OfType<Reference>()) {
string ext = reference.FileName.GetExtension();
if (language == null || !string.Equals(ext, lastExtension, StringComparison.OrdinalIgnoreCase)) {
lastExtension = ext;
language = SD.LanguageService.GetLanguageByExtension(ext);
}
IList<Conflict> currentConflicts = conflicts.GetOrDefault(reference);
if (currentConflicts == null)
currentConflicts = EmptyList<Conflict>.Instance;
var renamingContext = new RenameReferenceContext(reference, currentConflicts);
if (isLocal)
renamingContext.OldVariable = variable;
else
renamingContext.OldEntity = entity;
if (renamingContext != null)
language.CodeGenerator.RenameSymbol(renamingContext, newName);
}
}
public override string DisplayName {
get { return "Rename symbol"; }
}
}
}

117
src/Main/Base/Project/Refactoring/ICodeGenerator.cs

@ -2,7 +2,10 @@ @@ -2,7 +2,10 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.Semantics;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop.Project;
@ -21,6 +24,95 @@ namespace ICSharpCode.SharpDevelop.Refactoring @@ -21,6 +24,95 @@ namespace ICSharpCode.SharpDevelop.Refactoring
string GetPropertyName(string fieldName);
string GetParameterName(string fieldName);
string GetFieldName(string propertyName);
/// <summary>
/// Determines whether the given identifier is valid in the language or can be used if escaped properly.
/// </summary>
bool IsValidIdentifier(string identifier);
/// <summary>
/// Escapes the identifier if needed.
/// </summary>
string EscapeIdentifier(string identifier);
/// <summary>
/// Returns whether the given string is a keyword or not.
/// </summary>
bool IsKeyword(string identifier, bool treatContextualKeywordsAsIdentifiers = true);
void RenameSymbol(RenameReferenceContext context, string newName);
IEnumerable<Conflict> FindRenamingConflicts(Reference symbol, string newName);
}
public abstract class Conflict
{
public IVariable ConflictingVariable { get; private set; }
public IEntity ConflictingEntity { get; private set; }
// TODO : Please add something like ISymbol => (IVariable, IEntity)
public bool IsLocalConflict {
get {
return ConflictingVariable != null;
}
}
public abstract bool IsSolvableConflict {
get;
}
public abstract void Solve();
protected Conflict()
{
}
protected Conflict(IVariable variable)
{
this.ConflictingVariable = variable;
}
protected Conflict(IEntity entity)
{
this.ConflictingEntity = entity;
}
}
public class UnknownConflict : Conflict
{
public override bool IsSolvableConflict {
get {
return false;
}
}
public override void Solve()
{
throw new NotSupportedException();
}
}
public class RenameReferenceContext
{
public IReadOnlyList<Conflict> Conflicts { get; private set; }
public Reference RenameTarget { get; private set; }
// TODO : Please add something like ISymbol => (IVariable, IEntity)
public IVariable OldVariable { get; set; }
public IEntity OldEntity { get; set; }
public bool IsLocal {
get { return OldVariable != null; }
}
public bool HasConflicts {
get { return Conflicts.Any(); }
}
public RenameReferenceContext(Reference target, IList<Conflict> conflicts)
{
this.RenameTarget = target;
this.Conflicts = conflicts.AsReadOnly();
}
}
public class DefaultCodeGenerator : ICodeGenerator
@ -81,5 +173,30 @@ namespace ICSharpCode.SharpDevelop.Refactoring @@ -81,5 +173,30 @@ namespace ICSharpCode.SharpDevelop.Refactoring
else
return newName;
}
public virtual bool IsValidIdentifier(string identifier)
{
return false;
}
public virtual string EscapeIdentifier(string identifier)
{
throw new NotSupportedException("Feature not supported!");
}
public virtual bool IsKeyword(string identifier, bool treatContextualKeywordsAsIdentifiers = true)
{
return false;
}
public virtual void RenameSymbol(RenameReferenceContext context, string newName)
{
throw new NotSupportedException("Feature not supported!");
}
public virtual IEnumerable<Conflict> FindRenamingConflicts(Reference context, string newName)
{
throw new NotSupportedException("Feature not supported!");
}
}
}

8
src/Main/Base/Project/Src/Services/LanguageBinding/DefaultLanguageBinding.cs

@ -11,6 +11,14 @@ namespace ICSharpCode.SharpDevelop @@ -11,6 +11,14 @@ namespace ICSharpCode.SharpDevelop
{
public static readonly DefaultLanguageBinding DefaultInstance = new DefaultLanguageBinding();
public virtual string Name {
get { return ""; }
}
public virtual StringComparer IdentifierComparer {
get { return StringComparer.Ordinal; }
}
public virtual IFormattingStrategy FormattingStrategy {
get {
return DefaultFormattingStrategy.DefaultInstance;

14
src/Main/Base/Project/Src/Services/LanguageBinding/ILanguageBinding.cs

@ -12,6 +12,20 @@ namespace ICSharpCode.SharpDevelop @@ -12,6 +12,20 @@ namespace ICSharpCode.SharpDevelop
/// </summary>
public interface ILanguageBinding
{
/// <summary>
/// Gets the display name for the language.
/// </summary>
string Name {
get;
}
/// <summary>
/// Gets the comparer used to compare two identifiers in this language.
/// </summary>
StringComparer IdentifierComparer {
get;
}
/// <summary>
/// Provides access to the formatting strategy for this language.
/// </summary>

77
src/Main/Base/Project/Src/Services/RefactoringService/FindReferenceService.cs

@ -3,12 +3,14 @@ @@ -3,12 +3,14 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using ICSharpCode.Core;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
using ICSharpCode.NRefactory.Utils;
using ICSharpCode.SharpDevelop.Editor.Search;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Parser;
@ -22,7 +24,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring @@ -22,7 +24,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring
/// </summary>
public static class FindReferenceService
{
#region FindReferences
// #region FindReferences
static IEnumerable<IProject> GetProjectsThatCouldReferenceEntity(IEntity entity)
{
ISolution solution = ProjectService.OpenSolution;
@ -96,6 +98,57 @@ namespace ICSharpCode.SharpDevelop.Refactoring @@ -96,6 +98,57 @@ namespace ICSharpCode.SharpDevelop.Refactoring
progressMonitor);
}
public static Task FindReferencesToHierarchyAsync(IMember member, IProgressMonitor progressMonitor, Action<SearchedFile> callback)
{
if (member == null)
throw new ArgumentNullException("member");
if (progressMonitor == null)
throw new ArgumentNullException("progressMonitor");
if (callback == null)
throw new ArgumentNullException("callback");
return Task.WhenAll(FindAllMembers(member).Distinct().Select(m => FindReferencesAsync(m, progressMonitor, callback)));
}
public static IEnumerable<IMember> FindAllMembers(IMember member)
{
yield return member;
foreach (var ancestor in InheritanceHelper.GetBaseMembers(member, true)) {
yield return ancestor;
// TODO : produces duplicates
foreach (var derived in FindDerivedMembers(ancestor))
yield return derived;
}
foreach (var derived in FindDerivedMembers(member))
yield return derived;
}
static IEnumerable<IMember> FindDerivedMembers(IMember member)
{
ITypeDefinition definition = member.DeclaringTypeDefinition;
if (definition == null) yield break;
var tree = BuildDerivedTypesGraph(definition).ConvertToDerivedTypeTree();
var derivedMembers = TreeTraversal.PreOrder(tree, node => node.Children)
.Select(n => FindDerivedMember(member, n));
foreach (var otherMember in derivedMembers.Where(m => m != null))
yield return otherMember;
}
static IMember FindDerivedMember(IMember member, ITreeNode<ITypeDefinition> node)
{
// if (endPoint != null && (node.Content.Equals(endPoint) || node.Content.GetAllBaseTypeDefinitions().Any(td => endPoint.Equals(td))))
// return null;
var importedMember = node.Content.Compilation.Import(member);
if (importedMember == null) return null;
return InheritanceHelper.GetDerivedMember(importedMember, node.Content);
}
public static IObservable<SearchedFile> FindReferencesToHierarchy(IMember member, IProgressMonitor progressMonitor)
{
return ReactiveExtensions.CreateObservable<SearchedFile>(
(monitor, callback) => FindReferencesToHierarchyAsync(member, monitor, callback),
progressMonitor);
}
/// <summary>
/// Finds references to a local variable.
/// </summary>
@ -120,7 +173,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring @@ -120,7 +173,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring
(monitor, callback) => FindLocalReferencesAsync(variable, monitor).ContinueWith(t => callback(t.Result)),
progressMonitor);
}
#endregion
// #endregion
#region FindDerivedTypes
/// <summary>
@ -182,6 +235,26 @@ namespace ICSharpCode.SharpDevelop.Refactoring @@ -182,6 +235,26 @@ namespace ICSharpCode.SharpDevelop.Refactoring
}
}
/// <summary>
/// Builds a graph of type definitions.
/// </summary>
public static TypeGraphNode BuildFullTypeGraph(ITypeDefinition type)
{
if (type == null)
throw new ArgumentNullException("baseType");
var solutionSnapshot = GetSolutionSnapshot(type.Compilation);
var compilations = GetProjectsThatCouldReferenceEntity(type).Select(p => solutionSnapshot.GetCompilation(p));
var graph = BuildTypeInheritanceGraph(compilations);
TypeGraphNode node;
if (graph.TryGetValue(new AssemblyQualifiedTypeName(type), out node)) {
// only derived types were requested, so don't return the base types
// (this helps the GC to collect the unused parts of the graph more quickly)
return node;
} else {
return new TypeGraphNode(type);
}
}
/// <summary>
/// Builds a graph of all type definitions in the specified set of project contents.
/// </summary>

Loading…
Cancel
Save