Skip to content

Commit

Permalink
Merge pull request #25 from penev92/enumSupport
Browse files Browse the repository at this point in the history
Add support for  enum-type field values
  • Loading branch information
penev92 authored Oct 23, 2022
2 parents 4862bad + 081374c commit c657acb
Show file tree
Hide file tree
Showing 11 changed files with 314 additions and 18 deletions.
37 changes: 37 additions & 0 deletions server/Oraide.Core/Entities/Csharp/EnumInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
namespace Oraide.Core.Entities.Csharp
{
/// <summary>
/// Represents information about an enum type.
/// </summary>
public readonly struct EnumInfo
{
public readonly string Name;

public readonly string FullName;

public readonly string Description;

public readonly string[] Values;

public readonly bool IsFlagsEnum;

public readonly MemberLocation Location;

public EnumInfo(string name, string fullName, string description, string[] values, bool isFlagsEnum, MemberLocation location)
{
Name = name;
FullName = fullName;
Description = description;
Values = values;
IsFlagsEnum = isFlagsEnum;
Location = location;
}

public override string ToString() => FullName;

public string ToMarkdownInfoString() => "```csharp\n" +
$"enum {Name}" + (IsFlagsEnum ? " (flags)" : "") +
$"\n```\n" +
$"{Description}";
}
}
5 changes: 5 additions & 0 deletions server/Oraide.Csharp/CodeInformationProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ public ILookup<string, SimpleClassInfo> GetSpriteSequenceInfos()
{
return symbolGenerator.GetSpriteSequenceInfos();
}

public ILookup<string, EnumInfo> GetEnumInfos()
{
return symbolGenerator.GetEnums();
}

string GetOpenRaFolder(string workspaceFolderPath, string defaultOpenRaFolderPath)
{
Expand Down
38 changes: 29 additions & 9 deletions server/Oraide.Csharp/CodeParsers/RoslynCodeParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,18 @@ namespace Oraide.Csharp.CodeParsers
{
public static class RoslynCodeParser
{
public static (ILookup<string, TraitInfo>, WeaponInfo, ILookup<string, TraitInfo>, ILookup<string, SimpleClassInfo>) Parse(in string oraFolderPath)
public static (ILookup<string, TraitInfo>,
WeaponInfo, ILookup<string, TraitInfo>,
ILookup<string, SimpleClassInfo>,
ILookup<string, EnumInfo>
) Parse(in string oraFolderPath)
{
var traitInfos = new List<TraitInfo>();
var weaponInfoFields = Array.Empty<ClassFieldInfo>();
var warheadInfos = new List<SimpleClassInfo>();
var projectileInfos = new List<SimpleClassInfo>();
var spriteSequences = new List<SimpleClassInfo>();
var enumInfos = new List<EnumInfo>();

var filePaths = Directory.EnumerateFiles(oraFolderPath, "*.cs", SearchOption.AllDirectories)
.Where(x => !x.Contains("OpenRA.Test"));
Expand All @@ -32,9 +37,9 @@ public static (ILookup<string, TraitInfo>, WeaponInfo, ILookup<string, TraitInfo

foreach (var element in root.Members)
{
if (element is NamespaceDeclarationSyntax namespaceElement)
if (element is NamespaceDeclarationSyntax namespaceDeclaration)
{
foreach (var namespaceMember in namespaceElement.Members)
foreach (var namespaceMember in namespaceDeclaration.Members)
{
if (namespaceMember is ClassDeclarationSyntax classDeclaration)
{
Expand Down Expand Up @@ -96,6 +101,20 @@ public static (ILookup<string, TraitInfo>, WeaponInfo, ILookup<string, TraitInfo
spriteSequences.Add(newClassInfo);
}
}
else if (namespaceMember is EnumDeclarationSyntax enumDeclaration)
{
var name = enumDeclaration.Identifier.ValueText;
var namespaceName = namespaceDeclaration.Name.GetText().ToString().Trim();
var fullName = $"{namespaceName}.{name}";
var values = enumDeclaration.Members.Select(x => x.Identifier.ValueText).ToArray();
var isFlagsEnum = enumDeclaration.AttributeLists.Any(x => x.Attributes.Any(y => y.Name.GetText().ToString().Trim() == "Flags"));

// Some manual string nonsense to determine the class name location inside the file.
var enumDeclarationStart = enumDeclaration.GetLocation().SourceSpan.Start;
var enumDeclarationLocation = FindTypeLocationInText(filePath, fileText, name, enumDeclarationStart, "enum");

enumInfos.Add(new EnumInfo(name, fullName, "", values, isFlagsEnum, enumDeclarationLocation));
}
}
}
}
Expand Down Expand Up @@ -219,7 +238,8 @@ public static (ILookup<string, TraitInfo>, WeaponInfo, ILookup<string, TraitInfo
return (finalTraitInfos.ToLookup(x => x.TraitInfoName, y => y),
weaponInfo,
paletteTraitInfos,
finalSpriteSequenceInfos.ToLookup(x => x.Name, y => y));
finalSpriteSequenceInfos.ToLookup(x => x.Name, y => y),
enumInfos.ToLookup(x => x.Name, y => y));
}

// Files can potentially contain multiple TraitInfos.
Expand All @@ -246,7 +266,7 @@ static IEnumerable<TraitInfo> ParseTraitInfo(string filePath, string fileText, C

// Some manual string nonsense to determine trait name location inside the file.
var classStart = classDeclaration.GetLocation().SourceSpan.Start;
var classLocation = FindClassLocationInText(filePath, fileText, traitInfoName, classStart);
var classLocation = FindTypeLocationInText(filePath, fileText, traitInfoName, classStart);

yield return new TraitInfo(traitInfoName.Substring(0, traitInfoName.Length - 4), traitInfoName,
traitDesc, classLocation, baseTypes, traitProperties.ToArray(), isAbstract);
Expand Down Expand Up @@ -277,7 +297,7 @@ static SimpleClassInfo ParseSimpleClass(string filePath, string fileText, ClassD

// Some manual string nonsense to determine the class name location inside the file.
var classStart = classDeclaration.GetLocation().SourceSpan.Start;
var classLocation = FindClassLocationInText(filePath, fileText, projectileName, classStart);
var classLocation = FindTypeLocationInText(filePath, fileText, projectileName, classStart);
var baseTypes = ParseBaseTypes(classDeclaration).ToArray();
var fields = ParseClassFields(filePath, fileText, classDeclaration).ToArray();

Expand Down Expand Up @@ -406,13 +426,13 @@ static IEnumerable<string> ParseBaseTypes(ClassDeclarationSyntax classDeclaratio
}
}

static MemberLocation FindClassLocationInText(string filePath, string text, string traitInfoName, int definitionStartIndex)
static MemberLocation FindTypeLocationInText(string filePath, string text, string traitInfoName, int definitionStartIndex, string memberType = "class")
{
var subtext = text.Substring(0, definitionStartIndex);
subtext += text.Substring(definitionStartIndex, text.IndexOf($"class {traitInfoName}", definitionStartIndex, StringComparison.InvariantCulture) - definitionStartIndex);
subtext += text.Substring(definitionStartIndex, text.IndexOf($"{memberType} {traitInfoName}", definitionStartIndex, StringComparison.InvariantCulture) - definitionStartIndex);
var lines = subtext.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None);
var lineNumber = lines.Length;
var characterNumber = lines.Last().Length + 6;
var characterNumber = lines.Last().Length + memberType.Length + 1; // Add 1 for the space because reasons.
return new MemberLocation(filePath, lineNumber, characterNumber);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class CodeParsingSymbolGenerationStrategy : ICodeSymbolGenerationStrategy
WeaponInfo weaponInfo;
ILookup<string, TraitInfo> paletteTraitInfos;
ILookup<string, SimpleClassInfo> spriteSequenceInfos;
ILookup<string, EnumInfo> enumInfos;

public CodeParsingSymbolGenerationStrategy(string openRaFolder)
{
Expand Down Expand Up @@ -49,14 +50,23 @@ public ILookup<string, SimpleClassInfo> GetSpriteSequenceInfos()

return spriteSequenceInfos;
}

public ILookup<string, EnumInfo> GetEnums()
{
if (enumInfos == null)
Parse();

return enumInfos;
}

void Parse()
{
var (traits, weapons, palettes, spriteSequences) = RoslynCodeParser.Parse(openRaFolder);
var (traits, weapons, palettes, spriteSequences, enums) = RoslynCodeParser.Parse(openRaFolder);
traitInfos = traits;
weaponInfo = weapons;
paletteTraitInfos = palettes;
spriteSequenceInfos = spriteSequences;
enumInfos = enums;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class FromStaticFileSymbolGenerationStrategy : ICodeSymbolGenerationStrategy
WeaponInfo weaponInfo;
ILookup<string, TraitInfo> paletteTraitInfos;
ILookup<string, SimpleClassInfo> spriteSequenceInfos;
ILookup<string, EnumInfo> enumInfos;

public FromStaticFileSymbolGenerationStrategy(string openRaFolder)
{
Expand Down Expand Up @@ -122,6 +123,32 @@ public ILookup<string, SimpleClassInfo> GetSpriteSequenceInfos()
return typeInfos.ToLookup(x => x.Name, y => y);
}

public ILookup<string, EnumInfo> GetEnums()
{
if (enumInfos != null)
return enumInfos;

if (traitInfos == null)
GetTraitInfos();

if (weaponInfo.WeaponPropertyInfos == null)
GetWeaponInfo();

var traitEnums = traitsData["RelatedEnums"]!.Select(x =>
{
return new EnumInfo(x["Name"].ToString(), $"{x["Namespace"]}.{x["Name"]}", null,
x["Values"].Select(y => y["Value"].ToString()).ToArray(), false, NoLocation);
});

var weaponEnums = weaponsData["RelatedEnums"]!.Select(x =>
{
return new EnumInfo(x["Name"].ToString(), $"{x["Namespace"]}.{x["Name"]}", null,
x["Values"].Select(y => y["Value"].ToString()).ToArray(), false, NoLocation);
});

enumInfos = traitEnums.Union(weaponEnums).ToLookup(x => x.Name, y => y);
return enumInfos;
}

#region Private methods

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,7 @@ public interface ICodeSymbolGenerationStrategy
ILookup<string, TraitInfo> GetPaletteTraitInfos();

ILookup<string, SimpleClassInfo> GetSpriteSequenceInfos();

ILookup<string, EnumInfo> GetEnums();
}
}
8 changes: 7 additions & 1 deletion server/Oraide.LanguageServer/Caching/Entities/CodeSymbols.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,20 @@ public class CodeSymbols
/// SpriteSequence information grouped by name.
/// </summary>
public ILookup<string, SimpleClassInfo> SpriteSequenceInfos { get; }

/// <summary>
/// Enum type information grouped by type name.
/// </summary>
public ILookup<string, EnumInfo> EnumInfos { get; }

public CodeSymbols(ILookup<string, TraitInfo> traitInfos, WeaponInfo weaponInfo, ILookup<string, TraitInfo> paletteTraitInfos,
ILookup<string, SimpleClassInfo> spriteSequenceInfos)
ILookup<string, SimpleClassInfo> spriteSequenceInfos, ILookup<string, EnumInfo> enumInfos)
{
TraitInfos = traitInfos;
WeaponInfo = weaponInfo;
PaletteTraitInfos = paletteTraitInfos;
SpriteSequenceInfos = spriteSequenceInfos;
EnumInfos = enumInfos;
}
}
}
4 changes: 3 additions & 1 deletion server/Oraide.LanguageServer/Caching/SymbolCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,9 @@ IReadOnlyDictionary<string, ModData> CreateSymbolCachesPerMod()
var weaponInfo = codeInformationProvider.GetWeaponInfo();
var paletteTraitInfos = codeInformationProvider.GetPaletteTraitInfos();
var spriteSequenceInfos = codeInformationProvider.GetSpriteSequenceInfos();
var enumInfos = codeInformationProvider.GetEnumInfos();

var codeSymbols = new CodeSymbols(traitInfos, weaponInfo, paletteTraitInfos, spriteSequenceInfos);
var codeSymbols = new CodeSymbols(traitInfos, weaponInfo, paletteTraitInfos, spriteSequenceInfos, enumInfos);

var elapsedTotal = stopwatchTotal.Elapsed;
Console.Error.WriteLine($"Took {elapsedTotal} to load code symbols:");
Expand All @@ -64,6 +65,7 @@ IReadOnlyDictionary<string, ModData> CreateSymbolCachesPerMod()
Console.Error.WriteLine($" {weaponInfo.WarheadInfos.Length} warheadInfos");
Console.Error.WriteLine($" {paletteTraitInfos.Count} paletteTraitInfos");
Console.Error.WriteLine($" {spriteSequenceInfos.Count} spriteSequenceInfos");
Console.Error.WriteLine($" {enumInfos.Count} enumInfos");

var stopwatchYaml = new Stopwatch();
stopwatchYaml.Start();
Expand Down
Loading

0 comments on commit c657acb

Please sign in to comment.