/// <summary> /// Get hover content for an <see cref="MSBuildUnusedProperty"/>. /// </summary> /// <param name="unusedProperty"> /// The <see cref="MSBuildUnusedProperty"/>. /// </param> /// <returns> /// The content, or <c>null</c> if no content is provided. /// </returns> public MarkedStringContainer UnusedProperty(MSBuildUnusedProperty unusedProperty) { if (unusedProperty == null) { throw new ArgumentNullException(nameof(unusedProperty)); } List <MarkedString> content = new List <MarkedString> { $"Unused Property: `{unusedProperty.Name}` (condition is false)" }; string propertyHelp = MSBuildSchemaHelp.ForProperty(unusedProperty.Name); if (propertyHelp != null) { content.Add(propertyHelp); } content.Add( $"Value would have been: `{unusedProperty.Value}`" ); return(new MarkedStringContainer(content)); }
/// <summary> /// Get hover content for an <see cref="MSBuildProperty"/>. /// </summary> /// <param name="property"> /// The <see cref="MSBuildProperty"/>. /// </param> /// <returns> /// The content, or <c>null</c> if no content is provided. /// </returns> public MarkedStringContainer Property(MSBuildProperty property) { if (property == null) { throw new ArgumentNullException(nameof(property)); } List <MarkedString> content = new List <MarkedString> { $"Property: `{property.Name}`" }; string propertyHelp = MSBuildSchemaHelp.ForProperty(property.Name); if (propertyHelp != null) { content.Add(propertyHelp); } if (property.IsOverridden) { Position overridingDeclarationPosition = property.DeclaringXml.Location.ToNative(); StringBuilder overrideDescription = new StringBuilder(); string declarationFile = property.DeclaringXml.Location.File; if (declarationFile != property.Property.Xml.Location.File) { Uri declarationDocumentUri = VSCodeDocumentUri.FromFileSystemPath(declarationFile); overrideDescription.AppendLine( $"Value overridden at {overridingDeclarationPosition} in [{Path.GetFileName(declarationFile)}]({declarationDocumentUri})." ); } else { overrideDescription.AppendLine($"Value overridden at {overridingDeclarationPosition} in this file."); } overrideDescription.AppendLine(); overrideDescription.AppendLine(); overrideDescription.AppendLine( $"Unused value: `{property.DeclaringXml.Value}`" ); overrideDescription.AppendLine(); overrideDescription.AppendLine( $"Actual value: `{property.Value}`" ); content.Add(overrideDescription.ToString()); } else { content.Add($"Value: `{property.Value}`"); } return(new MarkedStringContainer(content)); }
/// <summary> /// Get property element completions. /// </summary> /// <param name="projectDocument"> /// The <see cref="ProjectDocument"/> for which completions will be offered. /// </param> /// <param name="replaceRange"> /// The range of text to be replaced by the completions. /// </param> /// <returns> /// A sequence of <see cref="CompletionItem"/>s. /// </returns> public IEnumerable <CompletionItem> GetCompletionItems(ProjectDocument projectDocument, Range replaceRange) { if (replaceRange == null) { throw new ArgumentNullException(nameof(replaceRange)); } LspModels.Range replaceRangeLsp = replaceRange.ToLsp(); HashSet <string> offeredPropertyNames = new HashSet <string>(); // Well-known properties. foreach (string wellKnownPropertyName in MSBuildSchemaHelp.WellKnownPropertyNames) { if (!offeredPropertyNames.Add(wellKnownPropertyName)) { continue; } yield return(PropertyCompletionItem(wellKnownPropertyName, replaceRangeLsp, description: MSBuildSchemaHelp.ForProperty(wellKnownPropertyName) )); } if (!projectDocument.HasMSBuildProject) { yield break; // Without a valid MSBuild project (even a cached one will do), we can't inspect existing MSBuild properties. } if (!projectDocument.Workspace.Configuration.Language.CompletionsFromProject.Contains(CompletionSource.Property)) { yield break; } int otherPropertyPriority = Priority + 10; string[] otherPropertyNames = projectDocument.MSBuildProject.Properties .Select(property => property.Name) .Where(propertyName => !propertyName.StartsWith("_")) // Ignore private properties. .ToArray(); foreach (string propertyName in otherPropertyNames) { if (!offeredPropertyNames.Add(propertyName)) { continue; } yield return(PropertyCompletionItem(propertyName, replaceRangeLsp, otherPropertyPriority, description: "Property defined in this project (or a project it imports)." )); } }
/// <summary> /// Get hover content for an <see cref="MSBuildUnusedProperty"/>. /// </summary> /// <param name="unusedProperty"> /// The <see cref="MSBuildUnusedProperty"/>. /// </param> /// <returns> /// The content, or <c>null</c> if no content is provided. /// </returns> public MarkedStringContainer UnusedProperty(MSBuildUnusedProperty unusedProperty) { if (unusedProperty == null) { throw new ArgumentNullException(nameof(unusedProperty)); } List <MarkedString> content = new List <MarkedString>(); if (unusedProperty.Element.HasParentPath(WellKnownElementPaths.DynamicPropertyGroup)) { content.Add( $"Dynamic Property: `{unusedProperty.Name}`" ); content.Add( "(properties declared in targets are only evaluated when building the project)" ); } else { content.Add( $"Unused Property: `{unusedProperty.Name}` (condition is false)" ); } string propertyHelp = MSBuildSchemaHelp.ForProperty(unusedProperty.Name); if (propertyHelp != null) { content.Add(propertyHelp); } content.Add( $"Value would have been: `{unusedProperty.Value}`" ); string helpLink = MSBuildSchemaHelp.HelpLinkForProperty(unusedProperty.Name); if (!String.IsNullOrWhiteSpace(helpLink)) { content.Add( $"[Help]({helpLink})" ); } return(new MarkedStringContainer(content)); }
/// <summary> /// Get hover content for an <see cref="MSBuildProperty"/>. /// </summary> /// <param name="property"> /// The <see cref="MSBuildProperty"/>. /// </param> /// <returns> /// The content, or <c>null</c> if no content is provided. /// </returns> public MarkedStringContainer Property(MSBuildProperty property) { if (property == null) { throw new ArgumentNullException(nameof(property)); } List <MarkedString> content = new List <MarkedString> { $"Property: `{property.Name}`" }; string propertyHelp = MSBuildSchemaHelp.ForProperty(property.Name); if (propertyHelp != null) { content.Add(propertyHelp); } if (property.IsOverridden) { // BUG: This is the location of the *overridden* property, not the *overriding* property. // We'll need to build a lookup by recursively following ProjectProperty.Predecessor. Position overridingDeclarationPosition = property.DeclaringXml.Location.ToNative(); StringBuilder overrideDescription = new StringBuilder(); string declarationFile = property.DeclaringXml.Location.File; if (declarationFile != property.Property.Xml.Location.File) { Uri declarationDocumentUri = VSCodeDocumentUri.FromFileSystemPath(declarationFile); overrideDescription.AppendLine( $"Value overridden at {overridingDeclarationPosition} in [{Path.GetFileName(declarationFile)}]({declarationDocumentUri})." ); } else { overrideDescription.AppendLine($"Value overridden at {overridingDeclarationPosition} in this file."); } overrideDescription.AppendLine(); overrideDescription.AppendLine(); overrideDescription.AppendLine( $"Unused value: `{property.DeclaringXml.Value}`" ); overrideDescription.AppendLine(); overrideDescription.AppendLine( $"Actual value: `{property.Value}`" ); content.Add(overrideDescription.ToString()); } else { content.Add($"Value: `{property.Value}`"); } string helpLink = MSBuildSchemaHelp.HelpLinkForProperty(property.Name); if (!String.IsNullOrWhiteSpace(helpLink)) { content.Add( $"[Help]({helpLink})" ); } return(new MarkedStringContainer(content)); }
/// <summary> /// Get property element completions. /// </summary> /// <param name="projectDocument"> /// The <see cref="ProjectDocument"/> for which completions will be offered. /// </param> /// <param name="replaceRange"> /// The range of text to be replaced by the completions. /// </param> /// <returns> /// A sequence of <see cref="CompletionItem"/>s. /// </returns> public IEnumerable <CompletionItem> GetCompletionItems(ProjectDocument projectDocument, Range replaceRange) { if (replaceRange == null) { throw new ArgumentNullException(nameof(replaceRange)); } LspModels.Range replaceRangeLsp = replaceRange.ToLsp(); HashSet <string> offeredPropertyNames = new HashSet <string>(); // Special-case properties // Output type yield return(new CompletionItem { Label = "<OutputType>", Detail = "Property", Kind = CompletionItemKind.Property, Documentation = MSBuildSchemaHelp.ForProperty("OutputType"), SortText = Priority + "<OutputType>", TextEdit = new TextEdit { NewText = "<OutputType>${1|Library,Exe|}</OutputType>", Range = replaceRangeLsp }, InsertTextFormat = InsertTextFormat.Snippet }); offeredPropertyNames.Add("OutputType"); // Target framework yield return(new CompletionItem { Label = "<TargetFramework>", Detail = "Property", Kind = CompletionItemKind.Property, Documentation = MSBuildSchemaHelp.ForProperty("TargetFramework"), SortText = Priority + "<TargetFramework>", TextEdit = new TextEdit { NewText = "<TargetFramework>${1|netstandard1.0,netstandard1.1,netstandard1.2,netstandard1.3,netstandard1.4,netstandard1.5,netstandard1.6,netstandard2.0,netcoreapp1.0,netcoreapp1.1,netcoreapp2.0,net4,net451,net452,net46,net461,net462,net47|}</TargetFramework>", Range = replaceRangeLsp }, InsertTextFormat = InsertTextFormat.Snippet }); offeredPropertyNames.Add("TargetFramework"); // Well-known (but standard-format) properties. foreach (string wellKnownPropertyName in MSBuildSchemaHelp.WellKnownPropertyNames) { if (!offeredPropertyNames.Add(wellKnownPropertyName)) { continue; } var propertyDefaults = MSBuildSchemaHelp.DefaultsForProperty(wellKnownPropertyName); yield return(PropertyCompletionItem(wellKnownPropertyName, replaceRangeLsp, description: MSBuildSchemaHelp.ForProperty(wellKnownPropertyName), defaultValue: propertyDefaults.defaultValue, defaultValues: propertyDefaults.defaultValues )); } if (!projectDocument.HasMSBuildProject) { yield break; // Without a valid MSBuild project (even a cached one will do), we can't inspect existing MSBuild properties. } if (!projectDocument.Workspace.Configuration.Language.CompletionsFromProject.Contains(CompletionSource.Property)) { yield break; } int otherPropertyPriority = Priority + 10; string[] otherPropertyNames = projectDocument.MSBuildProject.Properties .Select(property => property.Name) .Where(propertyName => !propertyName.StartsWith("_")) // Ignore private properties. .ToArray(); foreach (string propertyName in otherPropertyNames) { if (!offeredPropertyNames.Add(propertyName)) { continue; } yield return(PropertyCompletionItem(propertyName, replaceRangeLsp, otherPropertyPriority, description: $"I don't know anything about the '{propertyName}' property, but it's defined in this project (or a project that it imports); you can override its value by specifying it here." )); } }