From 7c52cb2b3d91bd16278bd054f9262e9fe129d543 Mon Sep 17 00:00:00 2001 From: Mark Carrington <31017244+MarkMpn@users.noreply.github.com> Date: Wed, 15 Nov 2023 20:31:33 +0000 Subject: [PATCH] Offer literal value suggestions for entityname attributes --- .../Autocomplete/Autocomplete.cs | 53 +++++++++++++++++-- MarkMpn.Sql4Cds.XTB/Autocomplete.cs | 52 ++++++++++++++++-- 2 files changed, 99 insertions(+), 6 deletions(-) diff --git a/MarkMpn.Sql4Cds.LanguageServer/Autocomplete/Autocomplete.cs b/MarkMpn.Sql4Cds.LanguageServer/Autocomplete/Autocomplete.cs index 76253508..e322d61c 100644 --- a/MarkMpn.Sql4Cds.LanguageServer/Autocomplete/Autocomplete.cs +++ b/MarkMpn.Sql4Cds.LanguageServer/Autocomplete/Autocomplete.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Reflection; using System.Text; +using Data8.PowerPlatform.Dataverse.Client.Wsdl; using MarkMpn.Sql4Cds.Engine; using Microsoft.Crm.Sdk.Messages; using Microsoft.VisualStudio.LanguageServer.Protocol; @@ -568,13 +569,15 @@ public IEnumerable GetSuggestions(string text, int pos) { // Check if there are any applicable filter operator functions that match the type of the current attribute var identifiers = prevPrevWord.Split('.'); + var instance = default(AutocompleteDataSource); + var entity = default(EntityMetadata); var attribute = default(AttributeMetadata); if (identifiers.Length == 2) { if (tables.TryGetValue(identifiers[0], out var tableName)) { - if (TryParseTableName(tableName, out var instanceName, out _, out tableName) && _dataSources.TryGetValue(instanceName, out var instance) && instance.Metadata.TryGetMinimalData(tableName, out var entity)) + if (TryParseTableName(tableName, out var instanceName, out _, out tableName) && _dataSources.TryGetValue(instanceName, out instance) && instance.Metadata.TryGetMinimalData(tableName, out entity)) { attribute = entity.Attributes.SingleOrDefault(a => a.LogicalName.Equals(identifiers[1], StringComparison.OrdinalIgnoreCase)); } @@ -584,7 +587,7 @@ public IEnumerable GetSuggestions(string text, int pos) { foreach (var table in tables.Values) { - if (TryParseTableName(table, out var instanceName, out _, out var tableName) && _dataSources.TryGetValue(instanceName, out var instance) && instance.Metadata.TryGetMinimalData(tableName, out var entity)) + if (TryParseTableName(table, out var instanceName, out _, out var tableName) && _dataSources.TryGetValue(instanceName, out instance) && instance.Metadata.TryGetMinimalData(tableName, out entity)) { var tableAttribute = entity.Attributes.SingleOrDefault(a => a.LogicalName.Equals(identifiers[0], StringComparison.OrdinalIgnoreCase)); @@ -623,8 +626,25 @@ public IEnumerable GetSuggestions(string text, int pos) items.AddRange(typeof(FetchXmlConditionMethods).GetMethods(BindingFlags.Public | BindingFlags.Static).Where(m => m.GetParameters()[0].ParameterType == expectedType).Select(m => new FunctionAutocompleteItem(m, currentLength))); } - if (attribute is EnumAttributeMetadata enumAttr) + if (attribute is EntityNameAttributeMetadata entityNameAttr) + { + var lookupAttr = entity.Attributes.SingleOrDefault(a => a.LogicalName == entityNameAttr.AttributeOf) as LookupAttributeMetadata; + + if (lookupAttr?.Targets == null || lookupAttr.Targets.Length == 0) + { + // Could be any entity name, show them all + items.AddRange(instance.Entities.Select(e => new EntityNameAutocompleteItem(e, currentLength))); + } + else + { + // Can only be one of the allowed entity types + items.AddRange(instance.Entities.Where(e => lookupAttr.Targets.Contains(e.LogicalName)).Select(e => new EntityNameAutocompleteItem(e, currentLength))); + } + } + else if (attribute is EnumAttributeMetadata enumAttr) + { items.AddRange(enumAttr.OptionSet.Options.Select(o => new OptionSetAutocompleteItem(o, currentLength))); + } } } @@ -1800,6 +1820,33 @@ public override string ToolTipText } } + class EntityNameAutocompleteItem : SqlAutocompleteItem + { + private readonly EntityMetadata _entity; + + public EntityNameAutocompleteItem(EntityMetadata entity, int replaceLength) : base(entity.LogicalName, replaceLength, CompletionItemKind.Text) + { + _entity = entity; + } + + public override string ToolTipTitle + { + get => _entity.DisplayName?.UserLocalizedLabel?.Label ?? _entity.LogicalName; + set => base.ToolTipTitle = value; + } + + public override string ToolTipText + { + get => _entity.Description?.UserLocalizedLabel?.Label; + set => base.ToolTipText = value; + } + + public override string GetTextForReplace() + { + return "'" + _entity.LogicalName + "'"; + } + } + class CollationAutocompleteItem : SqlAutocompleteItem { private readonly Collation _collation; diff --git a/MarkMpn.Sql4Cds.XTB/Autocomplete.cs b/MarkMpn.Sql4Cds.XTB/Autocomplete.cs index 492e40ff..1de56c50 100644 --- a/MarkMpn.Sql4Cds.XTB/Autocomplete.cs +++ b/MarkMpn.Sql4Cds.XTB/Autocomplete.cs @@ -568,13 +568,15 @@ public IEnumerable GetSuggestions(string text, int pos) { // Check if there are any applicable filter operator functions that match the type of the current attribute var identifiers = prevPrevWord.Split('.'); + var instance = default(AutocompleteDataSource); + var entity = default(EntityMetadata); var attribute = default(AttributeMetadata); if (identifiers.Length == 2) { if (tables.TryGetValue(identifiers[0], out var tableName)) { - if (TryParseTableName(tableName, out var instanceName, out _, out tableName) && _dataSources.TryGetValue(instanceName, out var instance) && instance.Metadata.TryGetMinimalData(tableName, out var entity)) + if (TryParseTableName(tableName, out var instanceName, out _, out tableName) && _dataSources.TryGetValue(instanceName, out instance) && instance.Metadata.TryGetMinimalData(tableName, out entity)) { attribute = entity.Attributes.SingleOrDefault(a => a.LogicalName.Equals(identifiers[1], StringComparison.OrdinalIgnoreCase)); } @@ -584,7 +586,7 @@ public IEnumerable GetSuggestions(string text, int pos) { foreach (var table in tables.Values) { - if (TryParseTableName(table, out var instanceName, out _, out var tableName) && _dataSources.TryGetValue(instanceName, out var instance) && instance.Metadata.TryGetMinimalData(tableName, out var entity)) + if (TryParseTableName(table, out var instanceName, out _, out var tableName) && _dataSources.TryGetValue(instanceName, out instance) && instance.Metadata.TryGetMinimalData(tableName, out entity)) { var tableAttribute = entity.Attributes.SingleOrDefault(a => a.LogicalName.Equals(identifiers[0], StringComparison.OrdinalIgnoreCase)); @@ -623,8 +625,25 @@ public IEnumerable GetSuggestions(string text, int pos) items.AddRange(typeof(FetchXmlConditionMethods).GetMethods(BindingFlags.Public | BindingFlags.Static).Where(m => m.GetParameters()[0].ParameterType == expectedType).Select(m => new FunctionAutocompleteItem(m, currentLength))); } - if (attribute is EnumAttributeMetadata enumAttr) + if (attribute is EntityNameAttributeMetadata entityNameAttr) + { + var lookupAttr = entity.Attributes.SingleOrDefault(a => a.LogicalName == entityNameAttr.AttributeOf) as LookupAttributeMetadata; + + if (lookupAttr?.Targets == null || lookupAttr.Targets.Length == 0) + { + // Could be any entity name, show them all + items.AddRange(instance.Entities.Select(e => new EntityNameAutocompleteItem(e, currentLength))); + } + else + { + // Can only be one of the allowed entity types + items.AddRange(instance.Entities.Where(e => lookupAttr.Targets.Contains(e.LogicalName)).Select(e => new EntityNameAutocompleteItem(e, currentLength))); + } + } + else if (attribute is EnumAttributeMetadata enumAttr) + { items.AddRange(enumAttr.OptionSet.Options.Select(o => new OptionSetAutocompleteItem(o, currentLength))); + } } } @@ -1782,6 +1801,33 @@ public override string ToolTipText } } + class EntityNameAutocompleteItem : SqlAutocompleteItem + { + private readonly EntityMetadata _entity; + + public EntityNameAutocompleteItem(EntityMetadata entity, int replaceLength) : base(entity.LogicalName, replaceLength, 11) + { + _entity = entity; + } + + public override string ToolTipTitle + { + get => _entity.DisplayName?.UserLocalizedLabel?.Label ?? _entity.LogicalName; + set => base.ToolTipTitle = value; + } + + public override string ToolTipText + { + get => _entity.Description?.UserLocalizedLabel?.Label; + set => base.ToolTipText = value; + } + + public override string GetTextForReplace() + { + return "'" + _entity.LogicalName + "'"; + } + } + class CollationAutocompleteItem : SqlAutocompleteItem { private readonly Collation _collation;