|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable disable
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.ExternalAccess.FSharp.SignatureHelp
{
internal class FSharpSignatureHelpItems
{
/// <summary>
/// The list of items to present to the user.
/// </summary>
public IList<FSharpSignatureHelpItem> Items { get; }
/// <summary>
/// The span this session applies to.
///
/// Navigation outside this span will cause signature help to be dismissed.
/// </summary>
public TextSpan ApplicableSpan { get; }
/// <summary>
/// Returns the specified argument index that the provided position is at in the current document. This
/// index may be greater than the number of arguments in the selected <see cref="FSharpSignatureHelpItem"/>.
/// </summary>
public int ArgumentIndex { get; }
/// <summary>
/// Returns the total number of arguments that have been typed in the current document. This may be
/// greater than the ArgumentIndex if there are additional arguments after the provided position.
/// </summary>
public int ArgumentCount { get; }
/// <summary>
/// Returns the name of specified argument at the current position in the document.
/// This only applies to languages that allow the user to provide named arguments.
/// If no named argument exists at the current position, then null should be returned.
///
/// This value is used to determine which documentation comment should be provided for the current
/// parameter. Normally this is determined simply by determining the parameter by index.
/// </summary>
public string ArgumentName { get; }
/// <summary>
/// The item to select by default. If this is <see langword="null"/> then the controller will
/// pick the first item that has enough arguments to be viable based on what argument
/// position the user is currently inside of.
/// </summary>
public int? SelectedItemIndex { get; }
public FSharpSignatureHelpItems(
IList<FSharpSignatureHelpItem> items,
TextSpan applicableSpan,
int argumentIndex,
int argumentCount,
string argumentName,
int? selectedItem = null)
{
Contract.ThrowIfNull(items);
Contract.ThrowIfTrue(items.IsEmpty());
Contract.ThrowIfTrue(selectedItem.HasValue && selectedItem.Value >= items.Count);
if (argumentIndex < 0)
{
throw new ArgumentException($"{nameof(argumentIndex)} < 0. {argumentIndex} < 0", nameof(argumentIndex));
}
if (argumentCount < argumentIndex)
{
throw new ArgumentException($"{nameof(argumentCount)} < {nameof(argumentIndex)}. {argumentCount} < {argumentIndex}", nameof(argumentIndex));
}
// Adjust the `selectedItem` index if duplicates are able to be removed.
var distinctItems = items.Distinct().ToList();
if (selectedItem.HasValue && items.Count != distinctItems.Count)
{
// `selectedItem` index has already been determined to be valid, it now needs to be adjusted to point
// to the equivalent item in the reduced list to account for duplicates being removed
// E.g.,
// items = {A, A, B, B, C, D}
// selectedItem = 4 (index for item C)
// ergo
// distinctItems = {A, B, C, D}
// actualItem = C
// selectedItem = 2 (index for item C)
var actualItem = items[selectedItem.Value];
selectedItem = distinctItems.IndexOf(actualItem);
Debug.Assert(selectedItem.Value >= 0, "actual item was not part of the final list");
}
this.Items = distinctItems;
this.ApplicableSpan = applicableSpan;
this.ArgumentIndex = argumentIndex;
this.ArgumentCount = argumentCount;
this.SelectedItemIndex = selectedItem;
this.ArgumentName = argumentName;
}
}
}
|