Skip to content

[API Proposal]: Customize editor visibility of a symbol based on the language. #72337

Open
@teo-tsirpanis

Description

@teo-tsirpanis

Background and motivation

I maintain a library that provides an idiomatic API for both C# and F#. For example if a method accepts a delegate, it will have an overload that accepts an equivalent F# FSharpFunc object. However both methods are visible in IntelliSense regardless of the language. It would be nice if the FSharpFunc overload was visible only in F# projects and the delegate overload was visible to projects of all languages except F#. I propose an attribute to allow that.

API Proposal

namespace System.Runtime.CompilerServices;

[System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Constructor |
System.AttributeTargets.Delegate | System.AttributeTargets.Enum | System.AttributeTargets.Event |
System.AttributeTargets.Field | System.AttributeTargets.Interface | System.AttributeTargets.Method |
System.AttributeTargets.Property | System.AttributeTargets.Struct, AllowMultiple = true)]
public class LanguageVisibleAttribute : Attribute
{
    public string Language { get; } // "C#", "F#" or "VB"
    public bool IsVisible { get; }
    public LanguageVisibleAttribute(string language, bool isVisible);
}

This attribute will override EditorBrowsableAttribute for a project if Language is the same with the project's language. To make a symbol visible in exclusively a set of languages, users have to also apply [EditorBrowsable(EditorBrowsableState.Never)] to hide it from the other languages.

API Usage

public class MyClass
{
    // This attribute controls the name the symbol will be available in F#.
    // The most sensible method for each language will have the shortest name.
    [Microsoft.FSharp.Core.CompilationSourceName("GetValueOrNull")]
    public object? GetValue();
    [Microsoft.FSharp.Core.CompilationSourceName("GetValue")]
    [EditorBrowsable(EditorBrowsableState.Never), LanguageVisible("F#", true)] // Make it visible only in F#.
    public FSharpValueOption<object> FSharpGetValue();

    // Hide it only from F#. A delegate will make sense in any other language.
    [LanguageVisible("F#", false)]
    public void MyMethod(Func<int, int, int> f);
    [EditorBrowsable(EditorBrowsableState.Never), LanguageVisible("F#", true)]
    public void MyMethod(FSharpFunc<int, FSharpFunc<int, int>> f);
}

Alternative Designs

  • Extending EditorBrowsableAttribute with a Language property and allowing it to be applied multiple times is not a good idea for two reasons. First, existing tools will be confused if they see this attribute more than once, and second I would like this attribute to be specified in source and matched by name, to enable using it in earlier frameworks.
  • In my examples I used this attribute to special-case visibility only for F#. Since there are only two major and actively evolved languages in .NET, perhaps this attribute should be something F#-specific and maintained by the F# team, but a general solution seems better.
  • VB could be renamed to Visual Basic. The former is recognized by dotnet new, and the latter is used by Roslyn's LanguageNames.

Risks

  • There might be some confusion of the recognized values of the language string. Users that didn't read the documentation might apply [LanguageVisible("FSharp", true)] and they would not immediately realize they made a mistake.
  • Should we hide all symbols that are not supported in Visual Basic? I wouldn't say so, features like ref structs are fundamentally not supported in Visual Basic and the IDEs can mechanically find and hide them if they desire. FSharpFunc on the other hand is a regular class that could be used from C#; it just feels weird and the experience is not good.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions