-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
386 additions
and
0 deletions.
There are no files selected for viewing
256 changes: 256 additions & 0 deletions
256
src/dotnet/ReSharperPlugin.RimworldDev/Refactoring/DefElementRenameFactory.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,256 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using JetBrains.Annotations; | ||
using JetBrains.Application; | ||
using JetBrains.Application.Progress; | ||
using JetBrains.Collections; | ||
using JetBrains.Diagnostics; | ||
using JetBrains.ReSharper.Feature.Services.Refactorings; | ||
using JetBrains.ReSharper.Feature.Services.Refactorings.Conflicts; | ||
using JetBrains.ReSharper.Feature.Services.Refactorings.Specific.Rename; | ||
using JetBrains.ReSharper.Feature.Services.Util; | ||
using JetBrains.ReSharper.Psi; | ||
using JetBrains.ReSharper.Psi.Pointers; | ||
using JetBrains.ReSharper.Psi.Resolve; | ||
using JetBrains.ReSharper.Psi.Tree; | ||
using JetBrains.ReSharper.Psi.Xml; | ||
using JetBrains.Util; | ||
|
||
namespace ReSharperPlugin.RimworldDev.Refactoring; | ||
|
||
[ShellFeaturePart] | ||
public class DefElementRenameFactory : AtomicRenamesFactory | ||
{ | ||
public override bool IsApplicable(IDeclaredElement declaredElement) | ||
{ | ||
return declaredElement.PresentationLanguage.Is<XmlLanguage>(); | ||
} | ||
|
||
public override IEnumerable<AtomicRenameBase> CreateAtomicRenames(IDeclaredElement declaredElement, string newName, | ||
bool doNotAddBindingConflicts) | ||
{ | ||
yield return new XmlDefAtomicRename(declaredElement, newName, doNotAddBindingConflicts); | ||
} | ||
|
||
public override RenameAvailabilityCheckResult CheckRenameAvailability( | ||
IDeclaredElement declaredElement) | ||
{ | ||
return RenameAvailabilityCheckResult.CanBeRenamed; | ||
// RenameAvailabilityCheckResult availabilityCheckResult = base.CheckRenameAvailability(declaredElement); | ||
// if ((EnumPattern) availabilityCheckResult != (EnumPattern) RenameAvailabilityCheckResult.CanBeRenamed) | ||
// return availabilityCheckResult; | ||
// switch (declaredElement) | ||
// { | ||
// case ITypeMember _: | ||
// return RenameAvailabilityCheckResult.CanBeRenamed; | ||
// case ITypeElement _: | ||
// return RenameAvailabilityCheckResult.CanBeRenamed; | ||
// case IXamlNamespaceAlias _: | ||
// return declaredElement.ShortName != "x" ? RenameAvailabilityCheckResult.CanBeRenamed : XamlRenameAvailabilityCheckResult.NotSupportedInXaml; | ||
// case IXamlResource _: | ||
// if (declaredElement.GetDeclarations().Count > 0) | ||
// return RenameAvailabilityCheckResult.CanBeRenamed; | ||
// break; | ||
// } | ||
// return XamlRenameAvailabilityCheckResult.NotSupportedInXaml; | ||
} | ||
} | ||
|
||
[PublicAPI] | ||
public class XmlDefAtomicRename : AtomicRenameBase | ||
{ | ||
[NotNull] private readonly IDeclaredElementPointer<IDeclaredElement> myOriginalElementPointer; | ||
private readonly bool myDoNotShowBindingConflicts; | ||
[NotNull] private readonly List<IReference> myNewReferences = new(); | ||
[NotNull] private readonly List<IDeclaration> myDeclarations = new(); | ||
[CanBeNull] private readonly List<IDeclaredElementPointer<IDeclaredElement>> mySecondaryElements; | ||
[CanBeNull] private DeclaredElementEnvoy<IDeclaredElement> myDeclaredElementEnvoy; | ||
[CanBeNull] private IDeclaredElementPointer<IDeclaredElement> myNewElementPointer; | ||
|
||
public override IDeclaredElement NewDeclaredElement => myNewElementPointer.NotNull().FindDeclaredElement(); | ||
|
||
public override IDeclaredElement PrimaryDeclaredElement | ||
{ | ||
get | ||
{ | ||
IDeclaredElement declaredElement = myOriginalElementPointer.FindDeclaredElement(); | ||
if (declaredElement != null) | ||
return declaredElement; | ||
return myDeclaredElementEnvoy?.GetValidDeclaredElement(); | ||
} | ||
} | ||
|
||
[NotNull] | ||
public override IList<IDeclaredElement> SecondaryDeclaredElements | ||
{ | ||
get | ||
{ | ||
return mySecondaryElements == null | ||
? EmptyList<IDeclaredElement>.Instance | ||
: mySecondaryElements | ||
.SelectNotNull( | ||
(Func<IDeclaredElementPointer<IDeclaredElement>, IDeclaredElement>) | ||
(x => x.FindDeclaredElement())).ToList(); | ||
} | ||
} | ||
|
||
public override string NewName { get; } | ||
|
||
public override string OldName { get; } | ||
|
||
|
||
public XmlDefAtomicRename( | ||
[NotNull] IDeclaredElement declaredElement, | ||
[NotNull] string newName, | ||
bool doNotShowBindingConflicts) | ||
{ | ||
myOriginalElementPointer = declaredElement.CreateElementPointer(); | ||
NewName = newName; | ||
OldName = declaredElement.ShortName; | ||
myDoNotShowBindingConflicts = doNotShowBindingConflicts; | ||
mySecondaryElements = RenameRefactoringService.Instance.GetRenameService(declaredElement.PresentationLanguage) | ||
.GetSecondaryElements(declaredElement, newName).ToList(x => x.CreateElementPointer()); | ||
BuildDeclarations(); | ||
} | ||
|
||
public override void Rename(IRenameRefactoring executer, IProgressIndicator progress, bool hasConflictsWithDeclarations, | ||
IRefactoringDriver driver) | ||
{ | ||
BuildDeclarations(); | ||
var declaredElement = myOriginalElementPointer.FindDeclaredElement(); | ||
if (declaredElement == null) | ||
return; | ||
var psiServices = declaredElement.GetPsiServices(); | ||
var elementReferences = executer.Workflow.GetElementReferences(PrimaryDeclaredElement); | ||
progress.Start(myDeclarations.Count + elementReferences.Count); | ||
foreach (var declaration in myDeclarations) | ||
{ | ||
InterruptableActivityCookie.CheckAndThrow(progress); | ||
executer.Workflow.LanguageSpecific[declaration.Language].SetName(declaration, NewName, executer); | ||
progress.Advance(); | ||
} | ||
|
||
psiServices.Caches.Update(); | ||
var newDeclaredElement = myDeclarations[0].DeclaredElement; | ||
myNewElementPointer = newDeclaredElement.CreateElementPointer(); | ||
myNewReferences.Clear(); | ||
PsiLanguageType key1; | ||
ISet<IReference> referenceSet; | ||
foreach (var referencesWithLanguage in LanguageUtil | ||
.SortReferencesWithLanguages(elementReferences.Where(x => x.IsValid()), psiServices)) | ||
{ | ||
referencesWithLanguage.Deconstruct(out key1, out referenceSet); | ||
var language = key1; | ||
var references = referenceSet; | ||
var rename = executer.Workflow.LanguageSpecific[language]; | ||
foreach (IGrouping<IPsiSourceFile, IReference> grouping1 in references.GetSortedReferences() | ||
.GroupBy( | ||
it => it.GetTreeNode().GetSourceFile())) | ||
{ | ||
IPsiSourceFile key2 = grouping1.Key; | ||
if (key2 != null) | ||
{ | ||
IGrouping<IPsiSourceFile, IReference> grouping = grouping1; | ||
psiServices.Caches.WithSyncUpdateFiltered(key2, () => | ||
{ | ||
foreach (IReference reference1 in grouping) | ||
{ | ||
// @TODO: Is this where we're meant to change the text?! | ||
IReference oldReferenceForConflict = reference1; | ||
InterruptableActivityCookie.CheckAndThrow(progress); | ||
if (reference1.IsValid()) | ||
{ | ||
IReference reference2 = rename.TransformProjectedInitializer(reference1); | ||
DeclaredElementInstance subst = GetSubst(newDeclaredElement, executer); | ||
IReference reference3 = !(subst != null) | ||
? rename.BindReference(reference2, newDeclaredElement) | ||
: (subst.Substitution.Domain.IsEmpty() | ||
? rename.BindReference(reference2, subst.Element) | ||
: rename.BindReference(reference2, subst.Element, subst.Substitution)); | ||
if (!(reference3 is IImplicitReference) && !hasConflictsWithDeclarations && | ||
!myDoNotShowBindingConflicts && !rename.IsAlias(newDeclaredElement) && | ||
!rename.IsCheckResolvedTo(reference3, newDeclaredElement)) | ||
driver.AddLateConflict( | ||
() => Conflict.Create(oldReferenceForConflict, | ||
ConflictType.CANNOT_UPDATE_USAGE_CONFLICT), "late bound"); | ||
myNewReferences.Insert(0, reference3); | ||
rename.AdditionalReferenceProcessing(newDeclaredElement, reference3, | ||
myNewReferences); | ||
} | ||
|
||
progress.Advance(); | ||
} | ||
}); | ||
} | ||
} | ||
} | ||
|
||
// foreach ((IDeclaredElement element, IList<IReference> source) in SecondaryDeclaredElements | ||
// .ToList( | ||
// (Func<IDeclaredElement, (IDeclaredElement, IList<IReference>)>)(secondaryElement => | ||
// (secondaryElement, executer.Workflow.GetElementReferences(secondaryElement))))) | ||
// { | ||
// IDeclaredElement updatedElement = | ||
// UpdateSecondaryElement(element, newDeclaredElement, executer) ?? element; | ||
// foreach (KeyValuePair<PsiLanguageType, ISet<IReference>> referencesWithLanguage in | ||
// LanguageUtil.SortReferencesWithLanguages( | ||
// source.Where(x => x.IsValid()), psiServices)) | ||
// { | ||
// referencesWithLanguage.Deconstruct(out key1, out referenceSet); | ||
// PsiLanguageType language = key1; | ||
// ISet<IReference> references = referenceSet; | ||
// RenameHelperBase rename = executer.Workflow.LanguageSpecific[language]; | ||
// foreach (IGrouping<IPsiSourceFile, IReference> grouping2 in references.GetSortedReferences() | ||
// .GroupBy( | ||
// it => it.GetTreeNode().GetSourceFile())) | ||
// { | ||
// IGrouping<IPsiSourceFile, IReference> grouping = grouping2; | ||
// psiServices.Caches.WithSyncUpdateFiltered(grouping.Key, () => | ||
// { | ||
// foreach (IReference reference in grouping) | ||
// { | ||
// InterruptableActivityCookie.CheckAndThrow(progress); | ||
// if (reference.IsValid()) | ||
// rename.TransformProjectedInitializer(reference).BindTo(updatedElement); | ||
// } | ||
// }); | ||
// } | ||
// } | ||
// } | ||
} | ||
|
||
private static DeclaredElementInstance GetSubst( | ||
IDeclaredElement element, | ||
IRenameRefactoring executer) | ||
{ | ||
return executer.Workflow.LanguageSpecific[element.PresentationLanguage].GetSubst(element); | ||
} | ||
|
||
[CanBeNull] | ||
private static IDeclaredElement UpdateSecondaryElement( | ||
IDeclaredElement element, | ||
IDeclaredElement newDeclaredElement, | ||
IRenameRefactoring executor) | ||
{ | ||
return executor.Workflow.LanguageSpecific[element.PresentationLanguage].UpdateSecondaryElement(element, newDeclaredElement); | ||
} | ||
|
||
private void BuildDeclarations() | ||
{ | ||
myDeclarations.Clear(); | ||
IDeclaredElement declaredElement = myOriginalElementPointer.FindDeclaredElement(); | ||
if (declaredElement == null) | ||
return; | ||
IList<IDeclaration> allDeclarations = new MultyPsiDeclarations(declaredElement).AllDeclarations; | ||
if (allDeclarations.Count == 0) | ||
{ | ||
Assertion.Fail("Element of type '{0}' has no declarations.", declaredElement.GetElementType()); | ||
} | ||
else | ||
{ | ||
foreach (IDeclaration declaration in allDeclarations) | ||
myDeclarations.Add(declaration); | ||
} | ||
} | ||
} |
130 changes: 130 additions & 0 deletions
130
src/dotnet/ReSharperPlugin.RimworldDev/Refactoring/RenameDef.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using JetBrains.Application.DataContext; | ||
using JetBrains.Application.Progress; | ||
using JetBrains.ProjectModel; | ||
using JetBrains.ProjectModel.DataContext; | ||
using JetBrains.ReSharper.Feature.Services.Refactorings; | ||
using JetBrains.ReSharper.Feature.Services.Refactorings.Specific.Rename; | ||
using JetBrains.ReSharper.Feature.Services.Resources; | ||
using JetBrains.ReSharper.Psi; | ||
using JetBrains.ReSharper.Psi.DataContext; | ||
using JetBrains.ReSharper.Psi.Pointers; | ||
using JetBrains.ReSharper.Psi.Resolve; | ||
using JetBrains.ReSharper.Refactorings.Rename; | ||
using JetBrains.TextControl; | ||
using JetBrains.TextControl.DataContext; | ||
using JetBrains.Util; | ||
|
||
namespace ReSharperPlugin.RimworldDev.Refactoring; | ||
|
||
[RefactoringWorkflowProvider] | ||
public class RenameDefProvider : IRefactoringWorkflowProvider | ||
{ | ||
public IEnumerable<IRefactoringWorkflow> CreateWorkflow(IDataContext dataContext) | ||
{ | ||
var solution = dataContext.GetData(ProjectModelDataConstants.SOLUTION); | ||
yield return new RenameDefWorkflow(solution, "RenameXmlDef"); | ||
} | ||
} | ||
|
||
public class RenameDefWorkflow(ISolution solution, string actionId) : RenameWorkflowBase(solution, actionId) | ||
{ | ||
public override bool IsAvailable(IDataContext context) | ||
{ | ||
return true; | ||
} | ||
|
||
private List<IDeclaredElement> GetDeclaredElements(IDataContext context) | ||
{ | ||
if (context == null) | ||
throw new ArgumentNullException(nameof(context)); | ||
|
||
var declaredElements = new List<IDeclaredElement>(); | ||
var data1 = context.GetData(TextControlDataConstants.TEXT_CONTROL); | ||
|
||
DataProvider = DataProvider ?? context.GetData(RenameRefactoringService.RenameDataProvider); | ||
if ((DataProvider == null || DataProvider.CanBeLocal) && data1 != null && | ||
RenameRefactoringService.Instance.CheckLocalRenameAvailability(context)) | ||
return new(); | ||
ICollection<IDeclaredElement> data2 = | ||
context.GetData(RenameRefactoringService.PRIMEVAL_DECLARED_ELEMENTS_TO_RENAME); | ||
if (data2.IsNullOrEmpty()) | ||
return new(); | ||
|
||
// @TODO: Check, can we make a rename factory to just **work** with the in-built renaming | ||
declaredElements = data2.Where(x => RenameRefactoringService.Instance.CheckRenameAvailability(x) == RenameAvailabilityCheckResult.CanBeRenamed).ToList(); | ||
// declaredElements = data2.ToList(); | ||
return declaredElements; | ||
} | ||
|
||
public override bool Initialize(IDataContext context) | ||
{ | ||
var declaredElements = GetDeclaredElements(context); | ||
if (declaredElements.Count == 0) | ||
return false; | ||
|
||
if (DataModel != null) | ||
return true; | ||
var primaryDeclaredElement = declaredElements.First(); | ||
var fileRenames = RenameRefactoringService.Instance.FileRenameProviders | ||
.SelectMany(provider => provider.GetFileRenames(primaryDeclaredElement, primaryDeclaredElement.ShortName)) | ||
.AsCollection(); | ||
|
||
var canHaveFileRenames = fileRenames.IsEmpty() ? RenameFilesOption.NothingToRename : | ||
fileRenames.All(r => r.AlwaysMustBeRenamed) ? RenameFilesOption.RenameWithoutConfirmation : | ||
RenameFilesOption.RenameWithConfirmation; | ||
|
||
var renameHelperBase = LanguageSpecific[RenameUtil.GetPsiLanguageTypeOrKnownLanguage(primaryDeclaredElement)]; | ||
var data = context.GetData(PsiDataConstants.REFERENCE); | ||
var occurrence = new RenameWorkflowPopupOccurrence(Strings.RenameThisOverload_Text, | ||
Strings.RenameInitialElementOnly_Text, new IDeclaredElement[1] { declaredElements.FirstOrDefault() }); | ||
|
||
var workflowPopupOccurrenceArray1 = new List<RenameWorkflowPopupOccurrence>() | ||
{ | ||
occurrence, | ||
new(Strings.RenameAllOverloads_Text, Strings.RenameInitialElementAndAllItsOverloads_Text, declaredElements) | ||
}.ToArray(); | ||
// var workflowPopupOccurrenceArray1 = renameHelperBase.GetPopupOccurences(declaredElements.FirstOrDefault()).AsArray(); | ||
if (workflowPopupOccurrenceArray1.Length > 1) | ||
{ | ||
RenameWorkflowPopupOccurrence workflowPopupOccurrence = null; | ||
if (DataProvider == null) | ||
{ | ||
workflowPopupOccurrence = ShowOccurrences(workflowPopupOccurrenceArray1, context); | ||
} | ||
else | ||
{ | ||
int usages = DataProvider.Usages; | ||
RenameWorkflowPopupOccurrence[] workflowPopupOccurrenceArray2 = workflowPopupOccurrenceArray1.AsArray(); | ||
if (usages >= 0 && workflowPopupOccurrenceArray2.Length > usages) | ||
workflowPopupOccurrence = workflowPopupOccurrenceArray2[usages]; | ||
} | ||
|
||
if (workflowPopupOccurrence == null) | ||
return false; | ||
IEnumerable<IDeclaredElement> second = workflowPopupOccurrence.Elements.SelectNotNull( | ||
(Func<IDeclaredElementPointer<IDeclaredElement>, IDeclaredElement>)(dep => dep.FindDeclaredElement())); | ||
declaredElements = declaredElements.Concat(second).AsList(); | ||
} | ||
|
||
DataModel = new RenameDataModel(declaredElements, data, canHaveFileRenames, WorkflowExecuterLifetime, Solution, | ||
renameHelperBase.GetOptionsModel(primaryDeclaredElement, data, WorkflowExecuterLifetime)); | ||
return true; | ||
} | ||
|
||
public override IRefactoringExecuter CreateRefactoring(IRefactoringDriver driver) | ||
{ | ||
return new RenameRefactoring(this, this.Solution, driver); | ||
} | ||
}; | ||
|
||
public class RenameDef(RenameWorkflowBase workFlow, ISolution solution, IRefactoringDriver driver) | ||
: RenameRefactoring(workFlow, solution, driver) | ||
{ | ||
public override bool Execute(IProgressIndicator pi) | ||
{ | ||
return base.Execute(pi); | ||
} | ||
}; |