Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion example-mod/.idea/.idea.AshAndDust/.idea/.name

This file was deleted.

4 changes: 0 additions & 4 deletions example-mod/.idea/.idea.AshAndDust/.idea/encodings.xml

This file was deleted.

8 changes: 0 additions & 8 deletions example-mod/.idea/.idea.AshAndDust/.idea/indexLayout.xml

This file was deleted.

6 changes: 0 additions & 6 deletions example-mod/.idea/.idea.AshAndDust/.idea/vcs.xml

This file was deleted.

4 changes: 2 additions & 2 deletions example-mod/Source/AshAndDust.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@
<Private>False</Private>
</Reference>
<Reference Include="Assembly-CSharp">
<HintPath>D:\SteamLibrary\steamapps\common\RimWorld\RimWorldWin64_Data\Managed\Assembly-CSharp.dll</HintPath>
<HintPath>..\..\..\RimWorldWin64_Data\Managed\Assembly-CSharp.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath>D:\SteamLibrary\steamapps\common\RimWorld\RimWorldWin64_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
<HintPath>..\..\..\RimWorldWin64_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using JetBrains.ProjectModel;
using JetBrains.ReSharper.Feature.Services.CodeCompletion.Infrastructure;
using JetBrains.ReSharper.Feature.Services.CodeCompletion.Infrastructure.LookupItems;
using JetBrains.ReSharper.Feature.Services.CSharp.CodeCompletion.Infrastructure;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.CSharp;
using JetBrains.ReSharper.Psi.CSharp.Impl.Tree;
using JetBrains.ReSharper.Psi.CSharp.Parsing;
using JetBrains.ReSharper.Psi.CSharp.Tree;
using JetBrains.ReSharper.Psi.Tree;
using ReSharperPlugin.RimworldDev.SymbolScope;
using ReSharperPlugin.RimworldDev.TypeDeclaration;

namespace ReSharperPlugin.RimworldDev.ItemCompletion;

[Language(typeof(CSharpLanguage))]
public class RimworldKeyedTranslationsCSharpItemProvider: ItemsProviderOfSpecificContext<CSharpCodeCompletionContext>
{
private static RimworldCSharpLookupFactory LookupFactory = new();

protected override bool IsAvailable(CSharpCodeCompletionContext context)
{
var node = context.NodeInFile;
if (!node.Language.IsLanguage(CSharpLanguage.Instance)) return false;
if (node is not CSharpGenericToken) return false;
if (node.NodeType != CSharpTokenType.STRING_LITERAL_REGULAR) return false;

return true;
}

protected override bool AddLookupItems(CSharpCodeCompletionContext context, IItemsCollector collector)
{
var node = context.NodeInFile;

if (node.Parent?.NextSibling?.NextSibling is not ICSharpIdentifier identifier ||
identifier.GetText() != "Translate")
return false;

var xmlSymbolTable = context.NodeInFile.GetSolution().GetComponent<RimworldKeyedTranslationSymbolScope>();

foreach (var key in xmlSymbolTable.GetKeys())
{
var nullableKeyTag = xmlSymbolTable.GetTranslationKey(key);
if (!nullableKeyTag.HasValue) continue;
var keyTag = nullableKeyTag.Value;

var lookup = LookupFactory.CreateDeclaredElementLookupItem(
context,
key,
new DeclaredElementInstance(new XMLTagDeclaredElement(keyTag.Tag, $"{keyTag.Language}/{keyTag.KeyName}", false))
);

collector.Add(lookup);
}

return base.AddLookupItems(context, collector);
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.CSharp;
using JetBrains.ReSharper.Psi.ExtensionsAPI;
using JetBrains.ReSharper.Psi.Search;
using JetBrains.ReSharper.Psi.Xml;
Expand All @@ -10,7 +11,8 @@ namespace ReSharperPlugin.RimworldDev.Navigation;
[PsiSharedComponent]
public class RimworldSearcherFactory(SearchDomainFactory searchDomainFactory) : DomainSpecificSearcherFactoryBase
{
public override bool IsCompatibleWithLanguage(PsiLanguageType languageType) => languageType.Is<XmlLanguage>();
public override bool IsCompatibleWithLanguage(PsiLanguageType languageType) =>
languageType.Is<XmlLanguage>() || languageType.Is<CSharpLanguage>();

public override ISearchDomain GetDeclaredElementSearchDomain(IDeclaredElement declaredElement)
{
Expand All @@ -21,8 +23,7 @@ public override IDomainSpecificSearcher CreateReferenceSearcher(
IDeclaredElementsSet elements,
ReferenceSearcherParameters referenceSearcherParameters)
{
elements = new DeclaredElementsSet(elements.Where(element =>
IsCompatibleWithLanguage(element.PresentationLanguage)));
elements = new DeclaredElementsSet(elements.Where(element => element.PresentationLanguage.Is<XmlLanguage>()));

return new CustomSearcher(this, elements, referenceSearcherParameters, false);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System.Linq;
using JetBrains.DataFlow;
using JetBrains.Lifetimes;
using JetBrains.ProjectModel;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.Caches;
using JetBrains.ReSharper.Psi.CSharp;
using JetBrains.ReSharper.Psi.CSharp.Tree;
using JetBrains.ReSharper.Psi.Resolve;
using JetBrains.ReSharper.Psi.Tree;
using JetBrains.ReSharper.Psi.Util;
using ReSharperPlugin.RimworldDev.SymbolScope;

namespace ReSharperPlugin.RimworldDev.TypeDeclaration;

[ReferenceProviderFactory]
public class RimworldCSharpKeyedTranslationProvider : IReferenceProviderFactory
{
public RimworldCSharpKeyedTranslationProvider(Lifetime lifetime) =>
Changed = new Signal<IReferenceProviderFactory>(lifetime, GetType().FullName);

Check warning on line 20 in src/dotnet/ReSharperPlugin.RimworldDev/References/RimworldCSharpKeyedTranslationProvider.cs

View workflow job for this annotation

GitHub Actions / Build

'Signal<IReferenceProviderFactory>.Signal(Lifetime, string)' is obsolete: 'Use overloads without lifetime.'

public IReferenceFactory CreateFactory(IPsiSourceFile sourceFile, IFile file, IWordIndex wordIndexForChecks)
{
return sourceFile.PrimaryPsiLanguage.Is<CSharpLanguage>()
? new RimworldCSharpKeyedTranslationReferenceFactory()
: null;
}

public ISignal<IReferenceProviderFactory> Changed { get; }
}

public class RimworldCSharpKeyedTranslationReferenceFactory : IReferenceFactory
{
public ReferenceCollection GetReferences(ITreeNode element, ReferenceCollection oldReferences)
{
if (element is not ICSharpLiteralExpression ||
element.NextSibling?.NextSibling is not ICSharpIdentifier identifier ||
identifier.GetText() != "Translate")
return new ReferenceCollection();

var key = element.GetUnquotedText();
var xmlSymbolTable = element.GetSolution().GetComponent<RimworldKeyedTranslationSymbolScope>();

if (!xmlSymbolTable.HasTranslationKey(key))
return new ReferenceCollection();

var tags = xmlSymbolTable.GetAllTagsForKey(key);
return new ReferenceCollection(
tags.Select(tag =>
new RimworldKeyedTranslationReference(element, tag.Tag, tag.Language, tag.KeyName
)).ToList()
);
}

public bool HasReference(ITreeNode element, IReferenceNameContainer names)
{
if (element.NodeType.ToString() != "TEXT") return false;
return !element.Parent.GetText().Contains("defName") && names.Contains(element.GetText());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using JetBrains.Annotations;
using JetBrains.ProjectModel;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.ExtensionsAPI.Resolve;
using JetBrains.ReSharper.Psi.Resolve;
using JetBrains.ReSharper.Psi.Tree;
using ReSharperPlugin.RimworldDev.SymbolScope;

namespace ReSharperPlugin.RimworldDev.TypeDeclaration;

public class RimworldKeyedTranslationReference : TreeReferenceBase<ITreeNode>
{
private readonly ITreeNode myTypeElement;

private string myName;
private string keyName;
private string language;

public RimworldKeyedTranslationReference([NotNull] ITreeNode owner, ITreeNode typeElement, string laguage, string keyName) : base(owner)
{
myTypeElement = typeElement;
myName = $"{laguage}/{keyName}";
this.keyName = keyName;
this.language = laguage;
}

public override ISymbolTable GetReferenceSymbolTable(bool useReferenceName)
{
var symbolScope = myOwner.GetSolution().GetComponent<RimworldKeyedTranslationSymbolScope>();

symbolScope.AddDeclaredElement(
myOwner.GetSolution(),
myTypeElement,
language,
keyName,
false
);

return symbolScope.GetSymbolTable(myOwner.GetSolution());
}

public override ResolveResultWithInfo ResolveWithoutCache()
{
return GetReferenceSymbolTable(true).GetResolveResult(GetName());
}

public override string GetName() => myName;

public override IReference BindTo(IDeclaredElement element) =>
BindTo(element, EmptySubstitution.INSTANCE);

public override IReference BindTo(
IDeclaredElement element,
ISubstitution substitution)
{
return this;
}

public override TreeTextRange GetTreeTextRange() => myOwner.GetTreeTextRange();

public override IAccessContext GetAccessContext() => new ElementAccessContext(myOwner);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System;
using System.Linq;
using JetBrains.DataFlow;
using JetBrains.Lifetimes;
using JetBrains.ProjectModel;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.Caches;
using JetBrains.ReSharper.Psi.Resolve;
using JetBrains.ReSharper.Psi.Tree;
using JetBrains.ReSharper.Psi.Web.WebConfig;
using JetBrains.ReSharper.Psi.Xml;
using JetBrains.ReSharper.Psi.Xml.Impl.Tree;
using ReSharperPlugin.RimworldDev.SymbolScope;

namespace ReSharperPlugin.RimworldDev.TypeDeclaration;

[ReferenceProviderFactory]
public class RimworldXmlKeyedTranslationReferenceProviderFactory : IReferenceProviderFactory
{
public RimworldXmlKeyedTranslationReferenceProviderFactory(Lifetime lifetime) =>
Changed = new Signal<IReferenceProviderFactory>(lifetime, GetType().FullName);

Check warning on line 21 in src/dotnet/ReSharperPlugin.RimworldDev/References/RimworldXmlKeyedTranslationReferenceProvider.cs

View workflow job for this annotation

GitHub Actions / Build

'Signal<IReferenceProviderFactory>.Signal(Lifetime, string)' is obsolete: 'Use overloads without lifetime.'

public IReferenceFactory CreateFactory(IPsiSourceFile sourceFile, IFile file, IWordIndex wordIndexForChecks)
{
return sourceFile.PrimaryPsiLanguage.Is<XmlLanguage>() && sourceFile.GetExtensionWithDot()
.Equals(".xml", StringComparison.CurrentCultureIgnoreCase)
? new RimworldXmlKeyedTranslationReferenceProvider()
: null;
}

public ISignal<IReferenceProviderFactory> Changed { get; }
}

// This reference provider only serves to attach a reference on the Keyed Translation to itself. It's a hacky workaround
// to allow us to do Find Usages on Keyed Translations
public class RimworldXmlKeyedTranslationReferenceProvider : IReferenceFactory
{
public ReferenceCollection GetReferences(ITreeNode element, ReferenceCollection oldReferences)
{
if (
element is not XmlIdentifier identifier ||
identifier?.Parent?.Parent?.Parent is not XmlTag { } LangaugeDataTag ||
LangaugeDataTag.Children().First() is not XmlTagHeaderNode languageDataHeaderNode ||
languageDataHeaderNode.Children().ElementAt(1) is not XmlIdentifier languageDataIdentifier ||
languageDataIdentifier.GetText() != "LanguageData"
)
return new ReferenceCollection();

var keyName = identifier.GetText();
var xmlSymbolTable = element.GetSolution().GetComponent<RimworldKeyedTranslationSymbolScope>();

var nullableTag = xmlSymbolTable.GetTranslationKey(keyName);
if (nullableTag is null)
return new ReferenceCollection();

var tag = nullableTag.Value;

return new ReferenceCollection(new RimworldKeyedTranslationReference(element, tag.Tag, tag.Language, tag.KeyName));
}

public bool HasReference(ITreeNode element, IReferenceNameContainer names)
{
if (element.NodeType.ToString() != "TEXT") return false;
return !element.Parent.GetText().Contains("defName") && names.Contains(element.GetText());
}
}
Loading
Loading