diff --git a/server/Oraide.Core/Entities/Csharp/EnumInfo.cs b/server/Oraide.Core/Entities/Csharp/EnumInfo.cs
new file mode 100644
index 0000000..7972719
--- /dev/null
+++ b/server/Oraide.Core/Entities/Csharp/EnumInfo.cs
@@ -0,0 +1,37 @@
+namespace Oraide.Core.Entities.Csharp
+{
+ ///
+ /// Represents information about an enum type.
+ ///
+ 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}";
+ }
+}
diff --git a/server/Oraide.Csharp/CodeInformationProvider.cs b/server/Oraide.Csharp/CodeInformationProvider.cs
index ece377a..4a84b1a 100644
--- a/server/Oraide.Csharp/CodeInformationProvider.cs
+++ b/server/Oraide.Csharp/CodeInformationProvider.cs
@@ -66,6 +66,11 @@ public ILookup GetSpriteSequenceInfos()
{
return symbolGenerator.GetSpriteSequenceInfos();
}
+
+ public ILookup GetEnumInfos()
+ {
+ return symbolGenerator.GetEnums();
+ }
string GetOpenRaFolder(string workspaceFolderPath, string defaultOpenRaFolderPath)
{
diff --git a/server/Oraide.Csharp/CodeParsers/RoslynCodeParser.cs b/server/Oraide.Csharp/CodeParsers/RoslynCodeParser.cs
index 0c01b6b..034aff2 100644
--- a/server/Oraide.Csharp/CodeParsers/RoslynCodeParser.cs
+++ b/server/Oraide.Csharp/CodeParsers/RoslynCodeParser.cs
@@ -12,13 +12,18 @@ namespace Oraide.Csharp.CodeParsers
{
public static class RoslynCodeParser
{
- public static (ILookup, WeaponInfo, ILookup, ILookup) Parse(in string oraFolderPath)
+ public static (ILookup,
+ WeaponInfo, ILookup,
+ ILookup,
+ ILookup
+ ) Parse(in string oraFolderPath)
{
var traitInfos = new List();
var weaponInfoFields = Array.Empty();
var warheadInfos = new List();
var projectileInfos = new List();
var spriteSequences = new List();
+ var enumInfos = new List();
var filePaths = Directory.EnumerateFiles(oraFolderPath, "*.cs", SearchOption.AllDirectories)
.Where(x => !x.Contains("OpenRA.Test"));
@@ -32,9 +37,9 @@ public static (ILookup, WeaponInfo, ILookup, WeaponInfo, ILookup 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));
+ }
}
}
}
@@ -219,7 +238,8 @@ public static (ILookup, WeaponInfo, ILookup 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.
@@ -246,7 +266,7 @@ static IEnumerable 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);
@@ -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();
@@ -406,13 +426,13 @@ static IEnumerable 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);
}
diff --git a/server/Oraide.Csharp/CodeSymbolGenerationStrategies/CodeParsingSymbolGenerationStrategy.cs b/server/Oraide.Csharp/CodeSymbolGenerationStrategies/CodeParsingSymbolGenerationStrategy.cs
index 28da27f..e7ddb55 100644
--- a/server/Oraide.Csharp/CodeSymbolGenerationStrategies/CodeParsingSymbolGenerationStrategy.cs
+++ b/server/Oraide.Csharp/CodeSymbolGenerationStrategies/CodeParsingSymbolGenerationStrategy.cs
@@ -12,6 +12,7 @@ class CodeParsingSymbolGenerationStrategy : ICodeSymbolGenerationStrategy
WeaponInfo weaponInfo;
ILookup paletteTraitInfos;
ILookup spriteSequenceInfos;
+ ILookup enumInfos;
public CodeParsingSymbolGenerationStrategy(string openRaFolder)
{
@@ -49,14 +50,23 @@ public ILookup GetSpriteSequenceInfos()
return spriteSequenceInfos;
}
+
+ public ILookup 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;
}
}
}
diff --git a/server/Oraide.Csharp/CodeSymbolGenerationStrategies/FromStaticFileSymbolGenerationStrategy.cs b/server/Oraide.Csharp/CodeSymbolGenerationStrategies/FromStaticFileSymbolGenerationStrategy.cs
index 2d42774..550c29d 100644
--- a/server/Oraide.Csharp/CodeSymbolGenerationStrategies/FromStaticFileSymbolGenerationStrategy.cs
+++ b/server/Oraide.Csharp/CodeSymbolGenerationStrategies/FromStaticFileSymbolGenerationStrategy.cs
@@ -22,6 +22,7 @@ class FromStaticFileSymbolGenerationStrategy : ICodeSymbolGenerationStrategy
WeaponInfo weaponInfo;
ILookup paletteTraitInfos;
ILookup spriteSequenceInfos;
+ ILookup enumInfos;
public FromStaticFileSymbolGenerationStrategy(string openRaFolder)
{
@@ -122,6 +123,32 @@ public ILookup GetSpriteSequenceInfos()
return typeInfos.ToLookup(x => x.Name, y => y);
}
+ public ILookup 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
diff --git a/server/Oraide.Csharp/CodeSymbolGenerationStrategies/ICodeSymbolGenerationStrategy.cs b/server/Oraide.Csharp/CodeSymbolGenerationStrategies/ICodeSymbolGenerationStrategy.cs
index bb19189..9dbed6d 100644
--- a/server/Oraide.Csharp/CodeSymbolGenerationStrategies/ICodeSymbolGenerationStrategy.cs
+++ b/server/Oraide.Csharp/CodeSymbolGenerationStrategies/ICodeSymbolGenerationStrategy.cs
@@ -12,5 +12,7 @@ public interface ICodeSymbolGenerationStrategy
ILookup GetPaletteTraitInfos();
ILookup GetSpriteSequenceInfos();
+
+ ILookup GetEnums();
}
}
diff --git a/server/Oraide.LanguageServer/Caching/Entities/CodeSymbols.cs b/server/Oraide.LanguageServer/Caching/Entities/CodeSymbols.cs
index 0e55b4d..e1d95c5 100644
--- a/server/Oraide.LanguageServer/Caching/Entities/CodeSymbols.cs
+++ b/server/Oraide.LanguageServer/Caching/Entities/CodeSymbols.cs
@@ -26,14 +26,20 @@ public class CodeSymbols
/// SpriteSequence information grouped by name.
///
public ILookup SpriteSequenceInfos { get; }
+
+ ///
+ /// Enum type information grouped by type name.
+ ///
+ public ILookup EnumInfos { get; }
public CodeSymbols(ILookup traitInfos, WeaponInfo weaponInfo, ILookup paletteTraitInfos,
- ILookup spriteSequenceInfos)
+ ILookup spriteSequenceInfos, ILookup enumInfos)
{
TraitInfos = traitInfos;
WeaponInfo = weaponInfo;
PaletteTraitInfos = paletteTraitInfos;
SpriteSequenceInfos = spriteSequenceInfos;
+ EnumInfos = enumInfos;
}
}
}
diff --git a/server/Oraide.LanguageServer/Caching/SymbolCache.cs b/server/Oraide.LanguageServer/Caching/SymbolCache.cs
index 904fd5a..c57026c 100644
--- a/server/Oraide.LanguageServer/Caching/SymbolCache.cs
+++ b/server/Oraide.LanguageServer/Caching/SymbolCache.cs
@@ -54,8 +54,9 @@ IReadOnlyDictionary 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:");
@@ -64,6 +65,7 @@ IReadOnlyDictionary 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();
diff --git a/server/Oraide.LanguageServer/LanguageServerProtocolHandlers/TextDocument/TextDocumentCompletionHandler.cs b/server/Oraide.LanguageServer/LanguageServerProtocolHandlers/TextDocument/TextDocumentCompletionHandler.cs
index 3b8820c..e599820 100644
--- a/server/Oraide.LanguageServer/LanguageServerProtocolHandlers/TextDocument/TextDocumentCompletionHandler.cs
+++ b/server/Oraide.LanguageServer/LanguageServerProtocolHandlers/TextDocument/TextDocumentCompletionHandler.cs
@@ -50,6 +50,20 @@ public class TextDocumentCompletionHandler : BaseRpcMessageHandler
CommitCharacters = new[] { ":" }
};
+ readonly CompletionItem trueCompletionItem = new ()
+ {
+ Label = "true",
+ Kind = CompletionItemKind.Value,
+ Detail = "A boolean value."
+ };
+
+ readonly CompletionItem falseCompletionItem = new ()
+ {
+ Label = "false",
+ Kind = CompletionItemKind.Value,
+ Detail = "A boolean value."
+ };
+
public TextDocumentCompletionHandler(SymbolCache symbolCache, OpenFileCache openFileCache)
: base(symbolCache, openFileCache) { }
@@ -316,6 +330,24 @@ protected override IEnumerable HandleRulesValue(CursorTarget cur
return sequences.Select(x => x.ToCompletionItem());
}
+ // Try to check if this is an enum type field.
+ var enumInfo = symbolCache[cursorTarget.ModId].CodeSymbols.EnumInfos.FirstOrDefault(x => x.Key == fieldInfo.InternalType);
+ if (enumInfo != null)
+ {
+ return enumInfo.FirstOrDefault().Values.Select(x => new CompletionItem
+ {
+ Label = x,
+ Kind = CompletionItemKind.EnumMember,
+ Detail = "Enum type value",
+ Documentation = $"{enumInfo.Key}.{x}"
+ });
+ }
+
+ if (fieldInfo.InternalType == "bool")
+ {
+ return new[] { trueCompletionItem, falseCompletionItem };
+ }
+
return Enumerable.Empty();
}
@@ -401,24 +433,24 @@ protected override IEnumerable HandleWeaponValue(CursorTarget cu
case 2:
{
ClassFieldInfo fieldInfo = default;
- var fieldInfos = Array.Empty();
+ var classFieldInfos = Array.Empty();
var parentNode = cursorTarget.TargetNode.ParentNode;
if (parentNode.Key == "Projectile")
{
- var projectileInfo = symbolCache[modId].CodeSymbols.WeaponInfo.ProjectileInfos.FirstOrDefault(x => x.Name == cursorTarget.TargetNode.ParentNode.Value);
+ var projectileInfo = weaponInfo.ProjectileInfos.FirstOrDefault(x => x.Name == parentNode.Value);
if (projectileInfo.Name != null)
{
fieldInfo = projectileInfo.PropertyInfos.FirstOrDefault(x => x.Name == cursorTarget.TargetNode.Key);
- fieldInfos = projectileInfo.PropertyInfos;
+ classFieldInfos = projectileInfo.PropertyInfos;
}
}
else if (parentNode.Key == "Warhead" || parentNode.Key.StartsWith("Warhead@"))
{
- var warheadInfo = symbolCache[modId].CodeSymbols.WeaponInfo.WarheadInfos.FirstOrDefault(x => x.Name == cursorTarget.TargetNode.ParentNode.Value);
+ var warheadInfo = weaponInfo.WarheadInfos.FirstOrDefault(x => x.Name == parentNode.Value);
if (warheadInfo.Name != null)
{
fieldInfo = warheadInfo.PropertyInfos.FirstOrDefault(x => x.Name == cursorTarget.TargetNode.Key);
- fieldInfos = warheadInfo.PropertyInfos;
+ classFieldInfos = warheadInfo.PropertyInfos;
}
}
@@ -441,7 +473,7 @@ protected override IEnumerable HandleWeaponValue(CursorTarget cu
// Pretend there is such a thing as a "SequenceImageReferenceAttribute" until we add it in OpenRA one day.
// NOTE: This will improve if/when we add the attribute.
- if (fieldInfos.Any(x => x.OtherAttributes.Any(y => y.Name == "SequenceReference"
+ if (classFieldInfos.Any(x => x.OtherAttributes.Any(y => y.Name == "SequenceReference"
&& (y.Value.Contains(',') ? y.Value.Substring(0, y.Value.IndexOf(',')) == fieldInfo.Name : y.Value == fieldInfo.Name))))
{
return spriteSequenceImageNames;
@@ -455,6 +487,24 @@ protected override IEnumerable HandleWeaponValue(CursorTarget cu
return sequences.Select(x => x.ToCompletionItem());
}
+ // Try to check if this is an enum type field.
+ var enumInfo = symbolCache[cursorTarget.ModId].CodeSymbols.EnumInfos.FirstOrDefault(x => x.Key == fieldInfo.InternalType);
+ if (enumInfo != null)
+ {
+ return enumInfo.FirstOrDefault().Values.Select(x => new CompletionItem
+ {
+ Label = x,
+ Kind = CompletionItemKind.EnumMember,
+ Detail = "Enum type value",
+ Documentation = $"{enumInfo.Key}.{x}"
+ });
+ }
+
+ if (fieldInfo.InternalType == "bool")
+ {
+ return new[] { trueCompletionItem, falseCompletionItem };
+ }
+
return Enumerable.Empty();
}
@@ -544,6 +594,37 @@ IEnumerable HandleSpriteSequenceProperty()
protected override IEnumerable HandleSpriteSequenceFileValue(CursorTarget cursorTarget)
{
+ IEnumerable HandleSpriteSequenceProperty()
+ {
+ var spriteSequenceFormat = symbolCache[cursorTarget.ModId].ModManifest.SpriteSequenceFormat.Type;
+ var spriteSequenceType = symbolCache[cursorTarget.ModId].CodeSymbols.SpriteSequenceInfos[spriteSequenceFormat].First();
+
+ var fieldInfo = spriteSequenceType.PropertyInfos.FirstOrDefault(x => x.Name == cursorTarget.TargetNode.Key);
+ if (fieldInfo.Name != null)
+ {
+ // Try to check if this is an enum type field.
+ var enumInfo = symbolCache[cursorTarget.ModId].CodeSymbols.EnumInfos
+ .FirstOrDefault(x => x.Key == fieldInfo.InternalType);
+ if (enumInfo != null)
+ {
+ return enumInfo.FirstOrDefault().Values.Select(x => new CompletionItem
+ {
+ Label = x,
+ Kind = CompletionItemKind.EnumMember,
+ Detail = "Enum type value",
+ Documentation = $"{enumInfo.Key}.{x}"
+ });
+ }
+ }
+
+ if (fieldInfo.InternalType == "bool")
+ {
+ return new[] { trueCompletionItem, falseCompletionItem };
+ }
+
+ return Enumerable.Empty();
+ }
+
switch (cursorTarget.TargetNodeIndentation)
{
case 1:
@@ -565,6 +646,17 @@ protected override IEnumerable HandleSpriteSequenceFileValue(Cur
return Enumerable.Empty();
}
+ case 2:
+ return HandleSpriteSequenceProperty();
+
+ case 4:
+ {
+ if (cursorTarget.TargetNode.ParentNode.ParentNode.Key == "Combine")
+ return HandleSpriteSequenceProperty();
+
+ return Enumerable.Empty();
+ }
+
default:
return Enumerable.Empty();
}
diff --git a/server/Oraide.LanguageServer/LanguageServerProtocolHandlers/TextDocument/TextDocumentDefinitionHandler.cs b/server/Oraide.LanguageServer/LanguageServerProtocolHandlers/TextDocument/TextDocumentDefinitionHandler.cs
index aaab872..c3dc8dc 100644
--- a/server/Oraide.LanguageServer/LanguageServerProtocolHandlers/TextDocument/TextDocumentDefinitionHandler.cs
+++ b/server/Oraide.LanguageServer/LanguageServerProtocolHandlers/TextDocument/TextDocumentDefinitionHandler.cs
@@ -199,6 +199,14 @@ protected override IEnumerable HandleRulesValue(CursorTarget cursorTar
.Where(x => x.Name == cursorTarget.TargetString)
.Select(x => x.Location.ToLspLocation(x.Name.Length));
}
+
+ // Try to check if this is an enum type field.
+ var enumInfo = symbolCache[cursorTarget.ModId].CodeSymbols.EnumInfos
+ .FirstOrDefault(x => x.Key == fieldInfo.InternalType);
+ if (enumInfo != null)
+ {
+ return new[] { enumInfo.First().Location.ToLspLocation(enumInfo.Key.Length) };
+ }
}
}
@@ -373,6 +381,14 @@ protected override IEnumerable HandleWeaponValue(CursorTarget cursorTa
.Select(x => x.Location.ToLspLocation(x.Name.Length));
}
+ // Try to check if this is an enum type field.
+ var enumInfo = symbolCache[cursorTarget.ModId].CodeSymbols.EnumInfos
+ .FirstOrDefault(x => x.Key == fieldInfo.InternalType);
+ if (enumInfo != null)
+ {
+ return new[] { enumInfo.First().Location.ToLspLocation(enumInfo.Key.Length) };
+ }
+
return Enumerable.Empty();
}
@@ -500,6 +516,26 @@ IEnumerable HandleSpriteSequenceProperty()
protected override IEnumerable HandleSpriteSequenceFileValue(CursorTarget cursorTarget)
{
+ IEnumerable HandleSpriteSequenceProperty()
+ {
+ var spriteSequenceFormat = symbolCache[cursorTarget.ModId].ModManifest.SpriteSequenceFormat.Type;
+ var spriteSequenceType = symbolCache[cursorTarget.ModId].CodeSymbols.SpriteSequenceInfos[spriteSequenceFormat].First();
+
+ var fieldInfo = spriteSequenceType.PropertyInfos.FirstOrDefault(x => x.Name == cursorTarget.TargetNode.Key);
+ if (fieldInfo.Name != null)
+ {
+ // Try to check if this is an enum type field.
+ var enumInfo = symbolCache[cursorTarget.ModId].CodeSymbols.EnumInfos
+ .FirstOrDefault(x => x.Key == fieldInfo.InternalType);
+ if (enumInfo != null)
+ {
+ return new[] { enumInfo.First().Location.ToLspLocation(enumInfo.Key.Length) };
+ }
+ }
+
+ return Enumerable.Empty();
+ }
+
switch (cursorTarget.TargetNodeIndentation)
{
// NOTE: Copied from HandleWeaponsFileKey.
@@ -524,6 +560,17 @@ protected override IEnumerable HandleSpriteSequenceFileValue(CursorTar
return Enumerable.Empty();
}
+ case 2:
+ return HandleSpriteSequenceProperty();
+
+ case 4:
+ {
+ if (cursorTarget.TargetNode.ParentNode.ParentNode.Key == "Combine")
+ return HandleSpriteSequenceProperty();
+
+ return Enumerable.Empty();
+ }
+
default:
return Enumerable.Empty();
}
diff --git a/server/Oraide.LanguageServer/LanguageServerProtocolHandlers/TextDocument/TextDocumentHoverHandler.cs b/server/Oraide.LanguageServer/LanguageServerProtocolHandlers/TextDocument/TextDocumentHoverHandler.cs
index cb122f5..e61491e 100644
--- a/server/Oraide.LanguageServer/LanguageServerProtocolHandlers/TextDocument/TextDocumentHoverHandler.cs
+++ b/server/Oraide.LanguageServer/LanguageServerProtocolHandlers/TextDocument/TextDocumentHoverHandler.cs
@@ -250,6 +250,14 @@ protected override Hover HandleRulesValue(CursorTarget cursorTarget)
var spriteSequence = image.Sequences.First(x => x.Name == cursorTarget.TargetString);
return HoverFromHoverInfo(spriteSequence.ToMarkdownInfoString(), range);
}
+
+ // Try to check if this is an enum type field.
+ var enumInfo = symbolCache[cursorTarget.ModId].CodeSymbols.EnumInfos.FirstOrDefault(x => x.Key == fieldInfo.InternalType);
+ if (enumInfo != null)
+ {
+ var content = $"```csharp\n{enumInfo.Key}.{cursorTarget.TargetString}\n```";
+ return HoverFromHoverInfo(content, range);
+ }
}
// Show explanation for world range value.
@@ -497,6 +505,14 @@ protected override Hover HandleWeaponValue(CursorTarget cursorTarget)
return HoverFromHoverInfo(spriteSequence.ToMarkdownInfoString(), range);
}
+ // Try to check if this is an enum type field.
+ var enumInfo = symbolCache[cursorTarget.ModId].CodeSymbols.EnumInfos.FirstOrDefault(x => x.Key == fieldInfo.InternalType);
+ if (enumInfo != null)
+ {
+ var content = $"```csharp\n{enumInfo.Key}.{cursorTarget.TargetString}\n```";
+ return HoverFromHoverInfo(content, range);
+ }
+
return null;
}
@@ -684,6 +700,27 @@ Hover HandleSpriteSequenceFileName()
return HoverFromHoverInfo(content, range);
}
+ Hover HandleSpriteSequenceProperty()
+ {
+ var spriteSequenceFormat = symbolCache[cursorTarget.ModId].ModManifest.SpriteSequenceFormat.Type;
+ var spriteSequenceType = symbolCache[cursorTarget.ModId].CodeSymbols.SpriteSequenceInfos[spriteSequenceFormat].First();
+
+ var fieldInfo = spriteSequenceType.PropertyInfos.FirstOrDefault(x => x.Name == cursorTarget.TargetNode.Key);
+ if (fieldInfo.Name != null)
+ {
+ // Try to check if this is an enum type field.
+ var enumInfo = symbolCache[cursorTarget.ModId].CodeSymbols.EnumInfos
+ .FirstOrDefault(x => x.Key == fieldInfo.InternalType);
+ if (enumInfo != null)
+ {
+ var content = $"```csharp\n{enumInfo.Key}.{cursorTarget.TargetString}\n```";
+ return HoverFromHoverInfo(content, range);
+ }
+ }
+
+ return null;
+ }
+
switch (cursorTarget.TargetNodeIndentation)
{
case 1:
@@ -711,6 +748,9 @@ Hover HandleSpriteSequenceFileName()
return null;
}
+ case 2:
+ return HandleSpriteSequenceProperty();
+
case 3:
{
if (cursorTarget.TargetNode.ParentNode.Key == "Combine")
@@ -719,6 +759,14 @@ Hover HandleSpriteSequenceFileName()
return null;
}
+ case 4:
+ {
+ if (cursorTarget.TargetNode.ParentNode.ParentNode.Key == "Combine")
+ return HandleSpriteSequenceProperty();
+
+ return null;
+ }
+
default:
return null;
}