|
// 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.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.Completion;
/// <summary>
/// The context presented to a <see cref="CompletionProvider"/> when providing completions.
/// </summary>
public sealed class CompletionContext
{
private readonly SegmentedList<CompletionItem> _items = [];
private CompletionItem? _suggestionModeItem;
private bool _isExclusive;
internal CompletionProvider Provider { get; }
/// <summary>
/// The document that completion was invoked within.
/// </summary>
public Document Document { get; }
/// <summary>
/// The caret position when completion was triggered.
/// </summary>
public int Position { get; }
/// <summary>
/// By providing this object, we have an opportunity to share requested SyntaxContext among all CompletionProviders
/// during a completion session to reduce repeat computation.
/// </summary>
private SharedSyntaxContextsWithSpeculativeModel? SharedSyntaxContextsWithSpeculativeModel { get; }
/// <summary>
/// The span of the syntax element at the caret position.
///
/// This is the most common value used for <see cref="CompletionItem.Span"/> and will
/// be automatically assigned to any <see cref="CompletionItem"/> that has no <see cref="CompletionItem.Span"/> specified.
/// </summary>
[Obsolete("Not used anymore. Use CompletionListSpan instead.", error: true)]
public TextSpan DefaultItemSpan { get; }
#pragma warning disable RS0030 // Do not used banned APIs
/// <summary>
/// The span of the document the completion list corresponds to. It will be set initially to
/// the result of <see cref="CompletionService.GetDefaultCompletionListSpan"/>, but it can
/// be overwritten during <see cref="CompletionService.GetCompletionsAsync(Document, int, CompletionTrigger, ImmutableHashSet{string}, OptionSet, CancellationToken)"/>.
/// The purpose of the span is to:
/// 1. Signify where the completions should be presented.
/// 2. Designate any existing text in the document that should be used for filtering.
/// 3. Specify, by default, what portion of the text should be replaced when a completion
/// item is committed.
/// </summary>
public TextSpan CompletionListSpan { get; set; }
#pragma warning restore
/// <summary>
/// The triggering action that caused completion to be started.
/// </summary>
public CompletionTrigger Trigger { get; }
/// <summary>
/// The options that completion was started with.
/// </summary>
internal CompletionOptions CompletionOptions { get; }
/// <summary>
/// The cancellation token to use for this operation.
/// </summary>
public CancellationToken CancellationToken { get; }
/// <summary>
/// Set to true if the items added here should be the only items presented to the user.
/// Expand items should never be exclusive.
/// </summary>
public bool IsExclusive
{
get
{
return _isExclusive && !Provider.IsExpandItemProvider;
}
set
{
if (value)
Debug.Assert(!Provider.IsExpandItemProvider);
_isExclusive = value;
}
}
/// <summary>
/// The options that completion was started with.
/// </summary>
public OptionSet Options { get; }
/// <summary>
/// Creates a <see cref="CompletionContext"/> instance.
/// </summary>
public CompletionContext(
CompletionProvider provider,
Document document,
int position,
TextSpan defaultSpan,
CompletionTrigger trigger,
OptionSet? options,
CancellationToken cancellationToken)
: this(provider ?? throw new ArgumentNullException(nameof(provider)),
document ?? throw new ArgumentNullException(nameof(document)),
position,
sharedSyntaxContextsWithSpeculativeModel: null,
defaultSpan,
trigger,
// Publicly available options do not affect this API.
CompletionOptions.Default,
cancellationToken)
{
#pragma warning disable RS0030 // Do not used banned APIs
Options = options ?? OptionSet.Empty;
#pragma warning restore
}
/// <summary>
/// Creates a <see cref="CompletionContext"/> instance.
/// </summary>
internal CompletionContext(
CompletionProvider provider,
Document document,
int position,
SharedSyntaxContextsWithSpeculativeModel? sharedSyntaxContextsWithSpeculativeModel,
TextSpan defaultSpan,
CompletionTrigger trigger,
in CompletionOptions options,
CancellationToken cancellationToken)
{
Provider = provider;
Document = document;
Position = position;
CompletionListSpan = defaultSpan;
Trigger = trigger;
CompletionOptions = options;
CancellationToken = cancellationToken;
SharedSyntaxContextsWithSpeculativeModel = sharedSyntaxContextsWithSpeculativeModel;
#pragma warning disable RS0030 // Do not used banned APIs
Options = OptionSet.Empty;
#pragma warning restore
}
internal IReadOnlyList<CompletionItem> Items => _items;
public void AddItem(CompletionItem item)
{
if (item == null)
{
throw new ArgumentNullException(nameof(item));
}
item = FixItem(item);
_items.Add(item);
}
public void AddItems(IEnumerable<CompletionItem> items)
{
if (items == null)
{
throw new ArgumentNullException(nameof(items));
}
foreach (var item in items)
{
AddItem(item);
}
}
/// <summary>
/// An optional <see cref="CompletionItem"/> that appears selected in the list presented to the user during suggestion mode.
///
/// Suggestion mode disables auto-selection of items in the list, giving preference to the text typed by the user unless a specific item is selected manually.
///
/// Specifying a <see cref="SuggestionModeItem"/> is a request that the completion host operate in suggestion mode.
/// The item specified determines the text displayed and the description associated with it unless a different item is manually selected.
///
/// No text is ever inserted when this item is completed, leaving the text the user typed instead.
/// </summary>
public CompletionItem? SuggestionModeItem
{
get
{
return _suggestionModeItem;
}
set
{
if (value != null)
{
value = FixItem(value);
}
_suggestionModeItem = value;
}
}
internal Task<SyntaxContext> GetSyntaxContextWithExistingSpeculativeModelAsync(Document document, CancellationToken cancellationToken)
{
if (SharedSyntaxContextsWithSpeculativeModel is null)
return Utilities.CreateSyntaxContextWithExistingSpeculativeModelAsync(document, Position, cancellationToken);
return SharedSyntaxContextsWithSpeculativeModel.GetSyntaxContextAsync(document, cancellationToken);
}
private CompletionItem FixItem(CompletionItem item)
{
// remember provider so we can find it again later
item.ProviderName = Provider.Name;
item.Span = CompletionListSpan;
return item;
}
}
|