Skip to content

Commit

Permalink
Adding some very rough WIP stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
Garethp committed May 10, 2024
1 parent 816f5e6 commit ee83e75
Show file tree
Hide file tree
Showing 2 changed files with 386 additions and 0 deletions.
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 src/dotnet/ReSharperPlugin.RimworldDev/Refactoring/RenameDef.cs
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);
}
};

0 comments on commit ee83e75

Please sign in to comment.