Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

System.CommandLine: Completions #84179

Open
adamsitnik opened this issue Mar 31, 2023 · 2 comments
Open

System.CommandLine: Completions #84179

adamsitnik opened this issue Mar 31, 2023 · 2 comments
Assignees
Milestone

Comments

@adamsitnik
Copy link
Member

Background and motivation

In #68578 (comment), we have introduced a set of symbol types for building a parsable hierarchy.
In #84177 (comment) a set of parsing APIs that expose information required by completions.

In this issue, we want to propose a set of APIs for completions.

API Proposal

namespace System.CommandLine.Completions;

The types presented in the details below are complete, none of our other proposals is extending them.

namespace System.CommandLine.Completions;

/// <summary>
/// Provides details about a command line completion item.
/// </summary>
/// reference: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_completion
public class CompletionItem : IEquatable<CompletionItem>
{
    /// <param name="label">The label value, which is the text displayed to users and, unless <paramref name="insertText"/> is set, is also used to populate the <see cref="InsertText"/> property.</param>
    /// <param name="kind">The kind of completion item.</param>
    /// <param name="sortText">The value used to sort the completion item in a list. If this is not provided, then <paramref name="label"/>  is used.</param>
    /// <param name="insertText">The text to be inserted by this completion item. If this is not provided, then <paramref name="label"/>  is used.</param>
    /// <param name="documentation">Documentation about the completion item.</param>
    /// <param name="detail">Additional details regarding the completion item.</param>
    public CompletionItem(string label, string kind = "Value", string? sortText = null, string? insertText = null, 
        string? documentation = null, string? detail = null);

    /// <summary>
    /// The label value, which is the text displayed to users.
    /// </summary>
    public string Label { get; }

    /// <summary>
    /// The kind of completion item.
    /// </summary>
    public string? Kind { get; }

    /// <summary>
    /// The value used to sort the completion item in a list.
    /// </summary>
    public string SortText { get; }

    /// <summary>
    /// The text to be inserted by this completion item.
    /// </summary>
    public string? InsertText { get; }

    /// <summary>
    /// Documentation about the completion item.
    /// </summary>
    public string? Documentation { get; set; }

    /// <summary>
    /// Additional details regarding the completion item.
    /// </summary>
    public string? Detail { get; }
}

/// <summary>
/// Supports command line completion operations.
/// </summary>
public class CompletionContext
{
    /// The text of the word to be completed, if any.
    public string WordToComplete { get; }

    /// The parse result for which completions are being requested.
    public ParseResult ParseResult { get; }

    /// <summary>
    /// Gets an empty CompletionContext.
    /// </summary>
    /// <remarks>Can be used for testing purposes.</remarks>
    public static CompletionContext Empty { get; }
}

/// <summary>
/// Provides details for calculating completions in the context of complete, unsplit command line text.
/// </summary>
public class TextCompletionContext : CompletionContext
{
    /// <summary>
    /// The position of the cursor within the command line. 
    /// </summary>
    public int CursorPosition { get; }

    /// <summary>
    /// The complete text of the command line prior to splitting, including any additional whitespace.
    /// </summary>
    public string CommandLineText { get; }

    /// <summary>
    /// Creates a new instance of <see cref="TextCompletionContext"/> at the specified cursor position.
    /// </summary>
    /// <param name="position">The cursor position at which completions are calculated.</param>
    public TextCompletionContext AtCursorPosition(int position);
}

namespace System.CommandLine

namespace System.CommandLine;

public abstract class CliSymbol
{
    /// <summary>
    /// Gets completions for the symbol.
    /// </summary>
    public abstract IEnumerable<CompletionItem> GetCompletions(CompletionContext context);
}

public abstract class CliArgument : CliSymbol
{
    /// <summary>
    /// Gets the list of completion sources for the argument.
    /// </summary>
    public List<Func<CompletionContext, IEnumerable<CompletionItem>>> CompletionSources { get; }
}

public abstract class CliOption : CliSymbol
{
    /// <summary>
    /// Gets the list of completion sources for the option.
    /// </summary>
    public List<Func<CompletionContext, IEnumerable<CompletionItem>>> CompletionSources { get; }
}

public sealed class ParseResult
{
    /// <summary>
    /// Gets completions based on a given parse result.
    /// </summary>
    /// <param name="position">The position at which completions are requested.</param>
    /// <returns>A set of completions for completion.</returns>
    public IEnumerable<CompletionItem> GetCompletions(int? position = null);
}

API Usage

Defining completion sources:

CliOption<string> fruitOption = new("--fruit");
fruitOption.CompletionSources.Add("apple", "banana", "cherry");

CliOption<string> vegetableOption = new("--vegetable");
vegetableOption.CompletionSources.Add("asparagus", "broccoli", "carrot");

CliCommand eatCommand = new ("eat")
{
    fruitOption,
    vegetableOption
};
@dotnet-issue-labeler dotnet-issue-labeler bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Mar 31, 2023
@ghost ghost added the untriaged New issue has not been triaged by the area owner label Mar 31, 2023
@ghost
Copy link

ghost commented Mar 31, 2023

Tagging subscribers to this area: @dotnet/area-system-console
See info in area-owners.md if you want to be subscribed.

Issue Details

Background and motivation

In #68578 (comment), we have introduced a set of symbol types for building a parsable hierarchy.
In #84177 (comment) a set of parsing APIs that expose information required by completions.

In this issue, we want to propose a set of APIs for completions.

API Proposal

namespace System.CommandLine.Completions;

The types presented in the details below are complete, none of our other proposals is extending them.

namespace System.CommandLine.Completions;

/// <summary>
/// Provides details about a command line completion item.
/// </summary>
/// reference: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_completion
public class CompletionItem : IEquatable<CompletionItem>
{
    /// <param name="label">The label value, which is the text displayed to users and, unless <paramref name="insertText"/> is set, is also used to populate the <see cref="InsertText"/> property.</param>
    /// <param name="kind">The kind of completion item.</param>
    /// <param name="sortText">The value used to sort the completion item in a list. If this is not provided, then <paramref name="label"/>  is used.</param>
    /// <param name="insertText">The text to be inserted by this completion item. If this is not provided, then <paramref name="label"/>  is used.</param>
    /// <param name="documentation">Documentation about the completion item.</param>
    /// <param name="detail">Additional details regarding the completion item.</param>
    public CompletionItem(string label, string kind = "Value", string? sortText = null, string? insertText = null, 
        string? documentation = null, string? detail = null);

    /// <summary>
    /// The label value, which is the text displayed to users.
    /// </summary>
    public string Label { get; }

    /// <summary>
    /// The kind of completion item.
    /// </summary>
    public string? Kind { get; }

    /// <summary>
    /// The value used to sort the completion item in a list.
    /// </summary>
    public string SortText { get; }

    /// <summary>
    /// The text to be inserted by this completion item.
    /// </summary>
    public string? InsertText { get; }

    /// <summary>
    /// Documentation about the completion item.
    /// </summary>
    public string? Documentation { get; set; }

    /// <summary>
    /// Additional details regarding the completion item.
    /// </summary>
    public string? Detail { get; }
}

/// <summary>
/// Supports command line completion operations.
/// </summary>
public class CompletionContext
{
    /// The text of the word to be completed, if any.
    public string WordToComplete { get; }

    /// The parse result for which completions are being requested.
    public ParseResult ParseResult { get; }

    /// <summary>
    /// Gets an empty CompletionContext.
    /// </summary>
    /// <remarks>Can be used for testing purposes.</remarks>
    public static CompletionContext Empty { get; }
}

/// <summary>
/// Provides details for calculating completions in the context of complete, unsplit command line text.
/// </summary>
public class TextCompletionContext : CompletionContext
{
    /// <summary>
    /// The position of the cursor within the command line. 
    /// </summary>
    public int CursorPosition { get; }

    /// <summary>
    /// The complete text of the command line prior to splitting, including any additional whitespace.
    /// </summary>
    public string CommandLineText { get; }

    /// <summary>
    /// Creates a new instance of <see cref="TextCompletionContext"/> at the specified cursor position.
    /// </summary>
    /// <param name="position">The cursor position at which completions are calculated.</param>
    public TextCompletionContext AtCursorPosition(int position);
}

namespace System.CommandLine

namespace System.CommandLine;

public abstract class CliSymbol
{
    /// <summary>
    /// Gets completions for the symbol.
    /// </summary>
    public abstract IEnumerable<CompletionItem> GetCompletions(CompletionContext context);
}

public abstract class CliArgument : CliSymbol
{
    /// <summary>
    /// Gets the list of completion sources for the argument.
    /// </summary>
    public List<Func<CompletionContext, IEnumerable<CompletionItem>>> CompletionSources { get; }
}

public abstract class CliOption : CliSymbol
{
    /// <summary>
    /// Gets the list of completion sources for the option.
    /// </summary>
    public List<Func<CompletionContext, IEnumerable<CompletionItem>>> CompletionSources { get; }
}

public sealed class ParseResult
{
    /// <summary>
    /// Gets completions based on a given parse result.
    /// </summary>
    /// <param name="position">The position at which completions are requested.</param>
    /// <returns>A set of completions for completion.</returns>
    public IEnumerable<CompletionItem> GetCompletions(int? position = null);
}

API Usage

Defining completion sources:

CliOption<string> fruitOption = new("--fruit");
fruitOption.CompletionSources.Add("apple", "banana", "cherry");

CliOption<string> vegetableOption = new("--vegetable");
vegetableOption.CompletionSources.Add("asparagus", "broccoli", "carrot");

CliCommand eatCommand = new ("eat")
{
    fruitOption,
    vegetableOption
};
Author: adamsitnik
Assignees: -
Labels:

area-System.Console, untriaged, needs-area-label

Milestone: -

@adamsitnik adamsitnik added api-ready-for-review API is ready for review, it is NOT ready for implementation and removed untriaged New issue has not been triaged by the area owner needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Mar 31, 2023
@adamsitnik adamsitnik self-assigned this Mar 31, 2023
@adamsitnik adamsitnik added this to the 8.0.0 milestone Apr 3, 2023
@terrajobst terrajobst added api-approved API was approved in API review, it can be implemented api-ready-for-review API is ready for review, it is NOT ready for implementation and removed api-ready-for-review API is ready for review, it is NOT ready for implementation api-approved API was approved in API review, it can be implemented labels Apr 6, 2023
@KieranDevvs
Copy link

Would these completions work out of the box with most shells on all platforms, or would it require some bootstrapping script like we saw with PowerShell in the earlier previews?

@terrajobst terrajobst removed this from the 8.0.0 milestone Jul 18, 2023
@ghost ghost added the untriaged New issue has not been triaged by the area owner label Jul 18, 2023
@jeffhandley jeffhandley removed the api-ready-for-review API is ready for review, it is NOT ready for implementation label Jul 20, 2023
@jeffhandley jeffhandley added this to the 9.0.0 milestone Jul 27, 2023
@ghost ghost removed the untriaged New issue has not been triaged by the area owner label Jul 27, 2023
@adamsitnik adamsitnik modified the milestones: 9.0.0, Future Jul 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants