From cd7ffe02da050c4bd30feb1124109259bbdaed3d Mon Sep 17 00:00:00 2001 From: penev92 Date: Sun, 25 Sep 2022 01:22:04 +0300 Subject: [PATCH 1/3] Added loading of enum type code symbols --- .../Oraide.Core/Entities/Csharp/EnumInfo.cs | 37 ++++++++++++++++++ .../Oraide.Csharp/CodeInformationProvider.cs | 5 +++ .../CodeParsers/RoslynCodeParser.cs | 38 ++++++++++++++----- .../CodeParsingSymbolGenerationStrategy.cs | 12 +++++- .../FromStaticFileSymbolGenerationStrategy.cs | 27 +++++++++++++ .../ICodeSymbolGenerationStrategy.cs | 2 + .../Caching/Entities/CodeSymbols.cs | 8 +++- .../Caching/SymbolCache.cs | 4 +- 8 files changed, 121 insertions(+), 12 deletions(-) create mode 100644 server/Oraide.Core/Entities/Csharp/EnumInfo.cs 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(); From 06f2bd10ee10dc4f74f0050e859f1f44cf075c52 Mon Sep 17 00:00:00 2001 From: penev92 Date: Sun, 23 Oct 2022 20:51:06 +0300 Subject: [PATCH 2/3] Added enum support in MiniYAML files This adds Hover/GoTo/Completion support for enums in Rules, Weapons and SpriteSequence files. --- .../TextDocumentCompletionHandler.cs | 75 +++++++++++++++++-- .../TextDocumentDefinitionHandler.cs | 47 ++++++++++++ .../TextDocument/TextDocumentHoverHandler.cs | 48 ++++++++++++ 3 files changed, 164 insertions(+), 6 deletions(-) diff --git a/server/Oraide.LanguageServer/LanguageServerProtocolHandlers/TextDocument/TextDocumentCompletionHandler.cs b/server/Oraide.LanguageServer/LanguageServerProtocolHandlers/TextDocument/TextDocumentCompletionHandler.cs index 3b8820c..d0b08ee 100644 --- a/server/Oraide.LanguageServer/LanguageServerProtocolHandlers/TextDocument/TextDocumentCompletionHandler.cs +++ b/server/Oraide.LanguageServer/LanguageServerProtocolHandlers/TextDocument/TextDocumentCompletionHandler.cs @@ -316,6 +316,19 @@ 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}" + }); + } + return Enumerable.Empty(); } @@ -401,24 +414,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 +454,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 +468,19 @@ 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}" + }); + } + return Enumerable.Empty(); } @@ -544,6 +570,32 @@ 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}" + }); + } + } + + return Enumerable.Empty(); + } + switch (cursorTarget.TargetNodeIndentation) { case 1: @@ -565,6 +617,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; } From 081374c0546e3f9122476cf7ec67c860e6ea6a76 Mon Sep 17 00:00:00 2001 From: penev92 Date: Sun, 23 Oct 2022 23:43:57 +0300 Subject: [PATCH 3/3] Added completion items for boolean fields --- .../TextDocumentCompletionHandler.cs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/server/Oraide.LanguageServer/LanguageServerProtocolHandlers/TextDocument/TextDocumentCompletionHandler.cs b/server/Oraide.LanguageServer/LanguageServerProtocolHandlers/TextDocument/TextDocumentCompletionHandler.cs index d0b08ee..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) { } @@ -329,6 +343,11 @@ protected override IEnumerable HandleRulesValue(CursorTarget cur }); } + if (fieldInfo.InternalType == "bool") + { + return new[] { trueCompletionItem, falseCompletionItem }; + } + return Enumerable.Empty(); } @@ -481,6 +500,11 @@ protected override IEnumerable HandleWeaponValue(CursorTarget cu }); } + if (fieldInfo.InternalType == "bool") + { + return new[] { trueCompletionItem, falseCompletionItem }; + } + return Enumerable.Empty(); } @@ -593,6 +617,11 @@ IEnumerable HandleSpriteSequenceProperty() } } + if (fieldInfo.InternalType == "bool") + { + return new[] { trueCompletionItem, falseCompletionItem }; + } + return Enumerable.Empty(); }