提交 6a796755 编写于 作者: C CyrusNajmabadi

Refactor completion API.

上级 d3af2aaa
......@@ -2556,11 +2556,11 @@ static void Main(string[] args)
var oldTree = await document.GetSyntaxTreeAsync();
var commit = await provider.GetChangeAsync(document, completionList.Items.First(i => i.DisplayText == "ToString()"), ' ');
var changes = commit.TextChanges;
var change = commit.TextChange;
// If we left the trailing trivia of the close curly of Main alone,
// there should only be one change: the replacement of "override " with a method.
Assert.Equal(changes.Single().Span, TextSpan.FromBounds(136, 145));
Assert.Equal(change.Span, TextSpan.FromBounds(136, 145));
}
}
}
......
......@@ -7,6 +7,7 @@
using System.Globalization;
using Microsoft.CodeAnalysis.Completion;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.Text;
......
......@@ -107,6 +107,7 @@ private async Task<Model> DoInBackgroundAsync(CancellationToken cancellationToke
}
return Model.CreateModel(
_documentOpt,
_disconnectedBufferGraph,
completionList,
selectedItem: completionList.Items.First(),
......
......@@ -35,7 +35,7 @@ public void SetModelBuilderState(bool includeBuilder)
.WithHardSelection(!softSelect);
}
return model.WithHardSelection(!softSelect).WithUseSuggestionCompletionMode(includeBuilder);
return model.WithHardSelection(!softSelect).WithUseSuggestionMode(includeBuilder);
}
}
}
......
......@@ -235,7 +235,12 @@ private void CommitItem(PresentationItem item)
return;
}
this.Commit(item, this.sessionOpt.Computation.InitialUnfilteredModel, commitChar: null);
this.Commit(
item, this.sessionOpt.Computation.InitialUnfilteredModel,
initialTextSnapshot: this.SubjectBuffer.CurrentSnapshot,
initialCaretPositionInView: this.TextView.Caret.Position.VirtualBufferPosition,
commitChar: null,
nextHandler: null);
}
private const int MaxMRUSize = 10;
......
......@@ -13,7 +13,8 @@ CommandState ICommandHandler<CommitUniqueCompletionListItemCommandArgs>.GetComma
return nextHandler();
}
void ICommandHandler<CommitUniqueCompletionListItemCommandArgs>.ExecuteCommand(CommitUniqueCompletionListItemCommandArgs args, Action nextHandler)
void ICommandHandler<CommitUniqueCompletionListItemCommandArgs>.ExecuteCommand(
CommitUniqueCompletionListItemCommandArgs args, Action nextHandler)
{
AssertIsForeground();
......@@ -51,7 +52,7 @@ void ICommandHandler<CommitUniqueCompletionListItemCommandArgs>.ExecuteCommand(C
if (model.IsUnique)
{
// We had a unique item in the list. Commit it and dismiss this session.
this.Commit(model.SelectedItem, model, commitChar: null);
this.CommitOnNonTypeChar(model.SelectedItem, model);
}
}
}
......
......@@ -95,7 +95,7 @@ private void CommitOnEnter(out bool sendThrough, out bool committed)
service.GetRules(), model.SelectedItem.Item, textTypedSoFar);
}
this.Commit(model.SelectedItem, model, commitChar: null);
this.CommitOnNonTypeChar(model.SelectedItem, model);
committed = true;
}
......
......@@ -172,7 +172,7 @@ private void CommitOnTab(out bool committed)
return;
}
Commit(model.SelectedItem, model, commitChar: null);
CommitOnNonTypeChar(model.SelectedItem, model);
committed = true;
}
}
......
......@@ -31,6 +31,9 @@ void ICommandHandler<TypeCharCommandArgs>.ExecuteCommand(TypeCharCommandArgs arg
AssertIsForeground();
var initialTextSnapshot = this.SubjectBuffer.CurrentSnapshot;
var initialVirtualCaretPosition = this.TextView.Caret.Position.VirtualBufferPosition;
var initialCaretPosition = GetCaretPointInViewBuffer();
// When a character is typed it is *always* sent through to the editor. This way the
......@@ -85,7 +88,8 @@ void ICommandHandler<TypeCharCommandArgs>.ExecuteCommand(TypeCharCommandArgs arg
{
Trace.WriteLine("typechar was on seam and a commit char, cannot have a completion session.");
this.CommitOnTypeChar(args.TypedChar);
this.CommitOnTypeChar(
args.TypedChar, initialTextSnapshot, initialVirtualCaretPosition, nextHandler);
return;
}
else if (_autoBraceCompletionChars.Contains(args.TypedChar) &&
......@@ -96,7 +100,8 @@ void ICommandHandler<TypeCharCommandArgs>.ExecuteCommand(TypeCharCommandArgs arg
// I don't think there is any better way than this. if typed char is one of auto brace completion char,
// we don't do multiple buffer change check
this.CommitOnTypeChar(args.TypedChar);
this.CommitOnTypeChar(
args.TypedChar, initialTextSnapshot, initialVirtualCaretPosition, nextHandler);
return;
}
else
......@@ -216,7 +221,8 @@ void ICommandHandler<TypeCharCommandArgs>.ExecuteCommand(TypeCharCommandArgs arg
// Known to be a commit character for the currently selected item. So just
// commit the session.
this.CommitOnTypeChar(args.TypedChar);
this.CommitOnTypeChar(
args.TypedChar, initialTextSnapshot, initialVirtualCaretPosition, nextHandler);
}
else
{
......@@ -311,23 +317,18 @@ private bool IsCommitCharacter(char ch)
}
var completionService = GetCompletionService();
var filterText = GetCurrentFilterText(model, model.SelectedItem.Item);
var textTypedSoFar = GetTextTypedSoFar(model, model.SelectedItem.Item);
return IsCommitCharacter(
completionService.GetRules(), model.SelectedItem.Item, ch, filterText);
completionService.GetRules(), model.SelectedItem.Item, ch, textTypedSoFar);
}
/// <summary>
/// Internal for testing purposes only.
/// </summary>
internal static bool IsCommitCharacter(
CompletionRules completionRules, CompletionItem item, char ch, string filterText)
CompletionRules completionRules, CompletionItem item, char ch, string textTypedSoFar)
{
// general rule: if the filtering text exactly matches the start of the item then it must be a filter character
if (TextTypedSoFarMatchesItem(item, ch, textTypedSoFar: filterText))
{
return false;
}
// First see if the item has any specifc commit rules it wants followed.
foreach (var rule in item.Rules.CommitCharacterRules)
{
switch (rule.Kind)
......@@ -351,6 +352,12 @@ private bool IsCommitCharacter(char ch)
}
}
// general rule: if the filtering text exactly matches the start of the item then it must be a filter character
if (TextTypedSoFarMatchesItem(item, ch, textTypedSoFar))
{
return false;
}
// Fall back to the default rules for this language's completion service.
return completionRules.DefaultCommitCharacters.IndexOf(ch) >= 0;
}
......@@ -371,28 +378,24 @@ private bool IsFilterCharacter(char ch)
return char.IsLetterOrDigit(ch);
}
var filterText = GetCurrentFilterText(model, model.SelectedItem.Item);
return IsFilterCharacter(model.SelectedItem.Item, ch, filterText);
var textTypedSoFar = GetTextTypedSoFar(model, model.SelectedItem.Item);
return IsFilterCharacter(model.SelectedItem.Item, ch, textTypedSoFar);
}
private static bool TextTypedSoFarMatchesItem(CompletionItem item, char ch, string textTypedSoFar)
{
var textTypedWithChar = textTypedSoFar + ch;
return item.DisplayText.StartsWith(textTypedWithChar, StringComparison.CurrentCultureIgnoreCase) ||
item.FilterText.StartsWith(textTypedWithChar, StringComparison.CurrentCultureIgnoreCase);
}
/// <summary>
/// Internal for testing purposes only.
/// </summary>
internal static bool IsFilterCharacter(CompletionItem item, char ch, string filterText)
{
// general rule: if the filtering text exactly matches the start of the item then it must be a filter character
if (TextTypedSoFarMatchesItem(item, ch, textTypedSoFar: filterText))
if (textTypedSoFar.Length > 0)
{
return false;
return item.DisplayText.StartsWith(textTypedSoFar, StringComparison.CurrentCultureIgnoreCase) ||
item.FilterText.StartsWith(textTypedSoFar, StringComparison.CurrentCultureIgnoreCase);
}
return false;
}
private static bool IsFilterCharacter(CompletionItem item, char ch, string textTypedSoFar)
{
// First see if the item has any specific filter rules it wants followed.
foreach (var rule in item.Rules.FilterCharacterRules)
{
switch (rule.Kind)
......@@ -416,10 +419,16 @@ internal static bool IsFilterCharacter(CompletionItem item, char ch, string filt
}
}
// general rule: if the filtering text exactly matches the start of the item then it must be a filter character
if (TextTypedSoFarMatchesItem(item, ch, textTypedSoFar))
{
return true;
}
return false;
}
private string GetCurrentFilterText(Model model, CompletionItem selectedItem)
private string GetTextTypedSoFar(Model model, CompletionItem selectedItem)
{
var textSnapshot = this.TextView.TextSnapshot;
var viewSpan = model.GetViewBufferSpan(selectedItem.Span);
......@@ -428,7 +437,10 @@ private string GetCurrentFilterText(Model model, CompletionItem selectedItem)
return filterText;
}
private void CommitOnTypeChar(char ch)
private void CommitOnTypeChar(
char ch, ITextSnapshot initialTextSnapshot,
VirtualSnapshotPoint initialCaretPointInView,
Action nextHandler)
{
AssertIsForeground();
......@@ -440,7 +452,8 @@ private void CommitOnTypeChar(char ch)
// was commit character if we had a selected item.
Contract.ThrowIfNull(model);
this.Commit(model.SelectedItem, model, ch);
this.Commit(model.SelectedItem, model, ch,
initialTextSnapshot, initialCaretPointInView, nextHandler);
}
}
}
......@@ -67,14 +67,7 @@ public override async Task<CompletionDescription> GetDescriptionAsync(Document d
var change = await service.GetChangeAsync(document, item, commitKey, cancellationToken).ConfigureAwait(false);
// normally the items that produce multiple changes are not expecting to trigger the behaviors that rely on looking at the text
if (change.TextChanges.Length == 1)
{
return change.TextChanges[0];
}
else
{
return new TextChange(item.Span, item.DisplayText);
}
return change.TextChange;
}
}
}
}
\ No newline at end of file
......@@ -17,6 +17,8 @@ internal class Model
private readonly DisconnectedBufferGraph _disconnectedBufferGraph;
public ITextSnapshot TriggerSnapshot { get { return _disconnectedBufferGraph.SubjectBufferSnapshot; } }
public Document TriggerDocument { get; }
public CompletionList OriginalList { get; }
public ImmutableArray<PresentationItem> TotalItems { get; }
public ImmutableArray<PresentationItem> FilteredItems { get; }
......@@ -45,6 +47,7 @@ internal class Model
public bool DismissIfEmpty { get; }
private Model(
Document triggerDocument,
DisconnectedBufferGraph disconnectedBufferGraph,
CompletionList originalList,
ImmutableArray<PresentationItem> totalItems,
......@@ -64,6 +67,7 @@ internal class Model
{
Contract.ThrowIfFalse(totalItems.Length != 0, "Must have at least one item.");
this.TriggerDocument = triggerDocument;
_disconnectedBufferGraph = disconnectedBufferGraph;
this.OriginalList = originalList;
this.TotalItems = totalItems;
......@@ -83,6 +87,7 @@ internal class Model
}
public static Model CreateModel(
Document triggerDocument,
DisconnectedBufferGraph disconnectedBufferGraph,
CompletionList originalList,
CompletionItem selectedItem,
......@@ -120,7 +125,7 @@ internal class Model
// By default we do not filter anything out.
ImmutableDictionary<CompletionItemFilter, bool> filterState = null;
if (completionService != null &&
workspace != null &&
workspace.Kind != WorkspaceKind.Interactive && // TODO (https://github.com/dotnet/roslyn/issues/5107): support in interactive
......@@ -152,9 +157,10 @@ internal class Model
var selectedPresentationItem = totalItems.FirstOrDefault(it => it.Item == selectedItem);
var completionItemToFilterText= new Dictionary<CompletionItem, string>();
var completionItemToFilterText = new Dictionary<CompletionItem, string>();
return new Model(
triggerDocument,
disconnectedBufferGraph,
originalList,
totalItems,
......@@ -196,86 +202,90 @@ public bool IsSoftSelection
}
}
private Model With(
Optional<ImmutableArray<PresentationItem>> filteredItems = default(Optional<ImmutableArray<PresentationItem>>),
Optional<PresentationItem> selectedItem = default(Optional<PresentationItem>),
Optional<ImmutableDictionary<CompletionItemFilter, bool>> filterState = default(Optional<ImmutableDictionary<CompletionItemFilter, bool>>),
Optional<IReadOnlyDictionary<CompletionItem, string>> completionItemToFilterText = default(Optional<IReadOnlyDictionary<CompletionItem, string>>),
Optional<bool> isHardSelection = default(Optional<bool>),
Optional<bool> isUnique = default(Optional<bool>),
Optional<bool> useSuggestionMode = default(Optional<bool>),
Optional<PresentationItem> suggestionModeItem = default(Optional<PresentationItem>),
Optional<ITrackingPoint> commitTrackingSpanEndPoint = default(Optional<ITrackingPoint>))
{
var newFilteredItems = filteredItems.HasValue ? filteredItems.Value : FilteredItems;
var newSelectedItem = selectedItem.HasValue ? selectedItem.Value : SelectedItem;
var newFilterState = filterState.HasValue ? filterState.Value : FilterState;
var newCompletionItemToFilterText = completionItemToFilterText.HasValue ? completionItemToFilterText.Value : CompletionItemToFilterText;
var newIsHardSelection = isHardSelection.HasValue ? isHardSelection.Value : IsHardSelection;
var newIsUnique = isUnique.HasValue ? isUnique.Value : IsUnique;
var newUseSuggestionMode = useSuggestionMode.HasValue ? useSuggestionMode.Value : UseSuggestionMode;
var newSuggestionModeItem = suggestionModeItem.HasValue ? suggestionModeItem.Value : SuggestionModeItem;
var newCommitTrackingSpanEndPoint = commitTrackingSpanEndPoint.HasValue ? commitTrackingSpanEndPoint.Value : CommitTrackingSpanEndPoint;
if (newFilteredItems == FilteredItems &&
newSelectedItem == SelectedItem &&
newFilterState == FilterState &&
newCompletionItemToFilterText == CompletionItemToFilterText &&
newIsHardSelection == IsHardSelection &&
newIsUnique == IsUnique &&
newUseSuggestionMode == UseSuggestionMode &&
newSuggestionModeItem == SuggestionModeItem &&
newCommitTrackingSpanEndPoint == CommitTrackingSpanEndPoint)
{
return this;
}
return new Model(
TriggerDocument, _disconnectedBufferGraph, OriginalList, TotalItems, newFilteredItems,
newSelectedItem, CompletionItemFilters, newFilterState, newCompletionItemToFilterText,
newIsHardSelection, newIsUnique, newUseSuggestionMode, newSuggestionModeItem,
DefaultSuggestionModeItem, Trigger, newCommitTrackingSpanEndPoint, DismissIfEmpty);
}
public Model WithFilteredItems(ImmutableArray<PresentationItem> filteredItems)
{
return new Model(_disconnectedBufferGraph, OriginalList, TotalItems, filteredItems,
filteredItems.FirstOrDefault(), CompletionItemFilters, FilterState, CompletionItemToFilterText, IsHardSelection,
IsUnique, UseSuggestionMode, SuggestionModeItem, DefaultSuggestionModeItem,
Trigger, CommitTrackingSpanEndPoint, DismissIfEmpty);
return With(filteredItems: filteredItems, selectedItem: filteredItems.FirstOrDefault());
}
public Model WithSelectedItem(PresentationItem selectedItem)
{
return selectedItem == this.SelectedItem
? this
: new Model(_disconnectedBufferGraph, OriginalList, TotalItems, FilteredItems,
selectedItem, CompletionItemFilters, FilterState, CompletionItemToFilterText, IsHardSelection, IsUnique,
UseSuggestionMode, SuggestionModeItem, DefaultSuggestionModeItem, Trigger,
CommitTrackingSpanEndPoint, DismissIfEmpty);
return With(selectedItem: selectedItem);
}
public Model WithHardSelection(bool isHardSelection)
{
return isHardSelection == this.IsHardSelection
? this
: new Model(_disconnectedBufferGraph, OriginalList, TotalItems, FilteredItems,
SelectedItem, CompletionItemFilters, FilterState, CompletionItemToFilterText, isHardSelection, IsUnique,
UseSuggestionMode, SuggestionModeItem, DefaultSuggestionModeItem, Trigger,
CommitTrackingSpanEndPoint, DismissIfEmpty);
return With(isHardSelection: isHardSelection);
}
public Model WithIsUnique(bool isUnique)
{
return isUnique == this.IsUnique
? this
: new Model(_disconnectedBufferGraph, OriginalList, TotalItems, FilteredItems,
SelectedItem, CompletionItemFilters, FilterState, CompletionItemToFilterText, IsHardSelection, isUnique,
UseSuggestionMode, SuggestionModeItem, DefaultSuggestionModeItem, Trigger,
CommitTrackingSpanEndPoint, DismissIfEmpty);
return With(isUnique: isUnique);
}
public Model WithSuggestionModeItem(PresentationItem suggestionModeItem)
{
return suggestionModeItem == this.SuggestionModeItem
? this
: new Model(_disconnectedBufferGraph, OriginalList, TotalItems, FilteredItems,
SelectedItem, CompletionItemFilters, FilterState, CompletionItemToFilterText, IsHardSelection, IsUnique,
UseSuggestionMode, suggestionModeItem, DefaultSuggestionModeItem, Trigger,
CommitTrackingSpanEndPoint, DismissIfEmpty);
return With(suggestionModeItem: suggestionModeItem);
}
public Model WithUseSuggestionCompletionMode(bool useSuggestionCompletionMode)
public Model WithUseSuggestionMode(bool useSuggestionMode)
{
return useSuggestionCompletionMode == this.UseSuggestionMode
? this
: new Model(_disconnectedBufferGraph, OriginalList, TotalItems, FilteredItems,
SelectedItem, CompletionItemFilters, FilterState, CompletionItemToFilterText, IsHardSelection, IsUnique,
useSuggestionCompletionMode, SuggestionModeItem, DefaultSuggestionModeItem, Trigger,
CommitTrackingSpanEndPoint, DismissIfEmpty);
return With(useSuggestionMode: useSuggestionMode);
}
internal Model WithTrackingSpanEnd(ITrackingPoint trackingSpanEnd)
{
return new Model(_disconnectedBufferGraph, OriginalList, TotalItems, FilteredItems,
SelectedItem, CompletionItemFilters, FilterState, CompletionItemToFilterText, IsHardSelection, IsUnique,
UseSuggestionMode, SuggestionModeItem, DefaultSuggestionModeItem, Trigger,
trackingSpanEnd, DismissIfEmpty);
return With(commitTrackingSpanEndPoint: new Optional<ITrackingPoint>(trackingSpanEnd));
}
internal Model WithFilterState(ImmutableDictionary<CompletionItemFilter, bool> filterState)
{
return new Model(_disconnectedBufferGraph, OriginalList, TotalItems, FilteredItems,
SelectedItem, CompletionItemFilters, filterState, CompletionItemToFilterText, IsHardSelection, IsUnique,
UseSuggestionMode, SuggestionModeItem, DefaultSuggestionModeItem, Trigger,
CommitTrackingSpanEndPoint, DismissIfEmpty);
return With(filterState: filterState);
}
internal Model WithCompletionItemToFilterText(IReadOnlyDictionary<CompletionItem, string> completionItemToFilterText)
{
return new Model(_disconnectedBufferGraph, OriginalList, TotalItems, FilteredItems,
SelectedItem, CompletionItemFilters, FilterState, completionItemToFilterText, IsHardSelection, IsUnique,
UseSuggestionMode, SuggestionModeItem, DefaultSuggestionModeItem, Trigger,
CommitTrackingSpanEndPoint, DismissIfEmpty);
return With(completionItemToFilterText: new Optional<IReadOnlyDictionary<CompletionItem, string>>(completionItemToFilterText));
}
internal SnapshotSpan GetCurrentSpanInSnapshot(ViewTextSpan originalSpan, ITextSnapshot textSnapshot)
......@@ -325,4 +335,4 @@ internal ViewTextSpan GetViewBufferSpan(TextSpan subjectBufferSpan)
return _disconnectedBufferGraph.GetSubjectBufferTextSpanInViewBuffer(subjectBufferSpan);
}
}
}
}
\ No newline at end of file
......@@ -346,7 +346,8 @@ private async Task VerifyCustomCommitProviderCheckResultsAsync(Document document
string actualExpectedCode = null;
MarkupTestFile.GetPosition(expectedCodeAfterCommit, out actualExpectedCode, out expectedCaretPosition);
if (commitChar.HasValue && !Controller.IsCommitCharacter(service.GetRules(), completionItem, commitChar.Value, string.Empty))
if (commitChar.HasValue &&
!Controller.IsCommitCharacter(service.GetRules(), completionItem, commitChar.Value, commitChar.Value.ToString()))
{
Assert.Equal(codeBeforeCommit, actualExpectedCode);
return;
......@@ -355,7 +356,7 @@ private async Task VerifyCustomCommitProviderCheckResultsAsync(Document document
var commit = await service.GetChangeAsync(document, completionItem, commitChar, CancellationToken.None);
var text = await document.GetTextAsync();
var newText = text.WithChanges(commit.TextChanges);
var newText = text.WithChanges(commit.TextChange);
var newDoc = document.WithText(newText);
document.Project.Solution.Workspace.TryApplyChanges(newDoc.Project.Solution);
......@@ -384,7 +385,8 @@ private async Task VerifyCustomCommitProviderCheckResultsAsync(Document document
string actualExpectedCode = null;
MarkupTestFile.GetPosition(expectedCodeAfterCommit, out actualExpectedCode, out expectedCaretPosition);
if (commitChar.HasValue && !Controller.IsCommitCharacter(service.GetRules(), completionItem, commitChar.Value, string.Empty))
if (commitChar.HasValue &&
!Controller.IsCommitCharacter(service.GetRules(), completionItem, commitChar.Value, commitChar.Value.ToString()))
{
Assert.Equal(codeBeforeCommit, actualExpectedCode);
return;
......@@ -435,7 +437,8 @@ private async Task VerifyCustomCommitProviderCheckResultsAsync(Document document
var text = await document.GetTextAsync();
if (commitChar == '\t' || Controller.IsCommitCharacter(service.GetRules(), firstItem, commitChar, textTypedSoFar))
if (commitChar == '\t' ||
Controller.IsCommitCharacter(service.GetRules(), firstItem, commitChar, textTypedSoFar + commitChar))
{
var textChange = await DescriptionModifyingPresentationItem.GetTextChangeAsync(
service, document, firstItem, commitChar);
......@@ -804,12 +807,14 @@ protected async Task VerifyCommitCharactersAsync(string initialMarkup, string te
foreach (var ch in validChars)
{
Assert.True(Controller.IsCommitCharacter(service.GetRules(), item, ch, textTypedSoFar), $"Expected '{ch}' to be a commit character");
Assert.True(Controller.IsCommitCharacter(
service.GetRules(), item, ch, textTypedSoFar + ch), $"Expected '{ch}' to be a commit character");
}
foreach (var ch in invalidChars)
{
Assert.False(Controller.IsCommitCharacter(service.GetRules(), item, ch, textTypedSoFar), $"Expected '{ch}' NOT to be a commit character");
Assert.False(Controller.IsCommitCharacter(
service.GetRules(), item, ch, textTypedSoFar + ch), $"Expected '{ch}' NOT to be a commit character");
}
}
}
......
......@@ -1586,9 +1586,6 @@ $$]]></Document>, extraExportedTypes:={GetType(CSharpEditorFormattingService)}.T
state.AssertMatchesTextStartingAtLine(6, " doodle;")
state.SendUndo()
Await state.WaitForAsynchronousOperationsAsync()
state.AssertMatchesTextStartingAtLine(6, "doodle;")
state.SendUndo()
Await state.WaitForAsynchronousOperationsAsync()
state.AssertMatchesTextStartingAtLine(6, "doo;")
End Using
End Function
......
......@@ -28,7 +28,7 @@ End Class
Await state.AssertSelectedCompletionItem("On Error GoTo", description:=String.Format(FeaturesResources.Keyword, "On Error GoTo") + vbCrLf + VBFeaturesResources.OnErrorGotoKeywordToolTip)
state.SendTypeChars(" ")
Await state.WaitForAsynchronousOperationsAsync()
Await state.AssertSelectedCompletionItem("Error GoTo", description:=String.Format(FeaturesResources.Keyword, "Error GoTo") + vbCrLf + VBFeaturesResources.OnErrorGotoKeywordToolTip)
Await state.AssertSelectedCompletionItem("On Error GoTo", description:=String.Format(FeaturesResources.Keyword, "On Error GoTo") + vbCrLf + VBFeaturesResources.OnErrorGotoKeywordToolTip)
End Using
End Function
......
......@@ -226,7 +226,7 @@ private ISet<string> GetExistingNamedParameters(AttributeArgumentListSyntax argu
return attributeType.GetAttributeNamedParameters(semanticModel.Compilation, within);
}
public override Task<TextChange?> GetTextChangeAsync(Document document, CompletionItem selectedItem, char? ch, CancellationToken cancellationToken)
protected override Task<TextChange?> GetTextChangeAsync(CompletionItem selectedItem, char? ch, CancellationToken cancellationToken)
{
return Task.FromResult(GetTextChange(selectedItem, ch));
}
......
......@@ -338,7 +338,7 @@ private CompletionItemRules GetRules(string displayText)
private static readonly string InsertionTextProperty = "insertionText";
public override Task<TextChange?> GetTextChangeAsync(Document document, CompletionItem selectedItem, char? ch, CancellationToken cancellationToken)
protected override Task<TextChange?> GetTextChangeAsync(CompletionItem selectedItem, char? ch, CancellationToken cancellationToken)
{
string insertionText;
if (!selectedItem.Properties.TryGetValue(InsertionTextProperty, out insertionText))
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Completion;
using Microsoft.CodeAnalysis.Completion.Providers;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Options;
......@@ -126,4 +123,4 @@ public override Task<CompletionDescription> GetDescriptionAsync(Document documen
return new TextChange(selectedItem.Span, selectedItem.DisplayText);
}
}
}
}
\ No newline at end of file
......@@ -235,11 +235,11 @@ int IEqualityComparer<IParameterSymbol>.GetHashCode(IParameterSymbol obj)
return obj.Name.GetHashCode();
}
public override Task<TextChange?> GetTextChangeAsync(Document document, CompletionItem selectedItem, char? ch, CancellationToken cancellationToken)
protected override Task<TextChange?> GetTextChangeAsync(CompletionItem selectedItem, char? ch, CancellationToken cancellationToken)
{
return Task.FromResult<TextChange?>(new TextChange(
selectedItem.Span,
selectedItem.DisplayText.Substring(0, selectedItem.DisplayText.Length - ColonString.Length)));
}
}
}
}
\ No newline at end of file
......@@ -220,4 +220,4 @@ protected override int GetTargetCaretPosition(SyntaxNode caretTarget)
}
}
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
......@@ -102,16 +101,16 @@ protected override async Task<AbstractSyntaxContext> CreateContext(Document docu
private static CompletionItemRules s_importDirectiveRules =
CompletionItemRules.Create(commitCharacterRules: ImmutableArray.Create(CharacterSetModificationRule.Create(CharacterSetModificationKind.Replace, '.', ';')));
// '<' should not filter the completion list, even though it's in generic items like IList<>
private static readonly CompletionItemRules s_itemRules = CompletionItemRules.Default.
WithFilterCharacterRule(CharacterSetModificationRule.Create(CharacterSetModificationKind.Remove, '<')).
WithCommitCharacterRule(CharacterSetModificationRule.Create(CharacterSetModificationKind.Add, '<'));
protected override CompletionItemRules GetCompletionItemRules(IReadOnlyList<ISymbol> symbols, AbstractSyntaxContext context)
{
if (context.IsInImportsDirective)
{
return s_importDirectiveRules;
}
else
{
return CompletionItemRules.Default;
}
return context.IsInImportsDirective
? s_importDirectiveRules
: s_itemRules;
}
}
}
}
\ No newline at end of file
......@@ -24,7 +24,9 @@ internal override bool IsInsertionTrigger(SourceText text, int characterPosition
return text[characterPosition] == '<';
}
protected override async Task<IEnumerable<CompletionItem>> GetItemsWorkerAsync(Document document, int position, TextSpan span, CompletionTrigger trigger, CancellationToken cancellationToken)
protected override async Task<IEnumerable<CompletionItem>> GetItemsWorkerAsync(
Document document, int position, TextSpan span,
CompletionTrigger trigger, CancellationToken cancellationToken)
{
var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
var token = tree.FindTokenOnLeftOfPosition(position, cancellationToken);
......
......@@ -5,6 +5,7 @@
using Microsoft.CodeAnalysis.Text;
using System.Collections.Immutable;
using System.Threading;
using System;
namespace Microsoft.CodeAnalysis.Completion
{
......@@ -44,12 +45,17 @@ public override async Task<CompletionChange> GetChangeAsync(Document document, C
{
var change = (await GetTextChangeAsync(document, item, commitKey, cancellationToken).ConfigureAwait(false))
?? new TextChange(item.Span, item.DisplayText);
return CompletionChange.Create(ImmutableArray.Create(change));
return CompletionChange.Create(change);
}
public virtual Task<TextChange?> GetTextChangeAsync(Document document, CompletionItem selectedItem, char? ch, CancellationToken cancellationToken)
{
return GetTextChangeAsync(selectedItem, ch, cancellationToken);
}
protected virtual Task<TextChange?> GetTextChangeAsync(CompletionItem selectedItem, char? ch, CancellationToken cancellationToken)
{
return Task.FromResult<TextChange?>(null);
}
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.Completion
......@@ -12,8 +14,11 @@ namespace Microsoft.CodeAnalysis.Completion
public sealed class CompletionChange
{
/// <summary>
/// The text changes to be applied to the document.
/// The text change to be applied to the document.
/// </summary>
public TextChange TextChange { get; }
[Obsolete("Use TextChange instead")]
public ImmutableArray<TextChange> TextChanges { get; }
/// <summary>
......@@ -23,16 +28,25 @@ public sealed class CompletionChange
public int? NewPosition { get; }
/// <summary>
/// True if the changes include the typed character that caused the <see cref="CompletionItem"/> to be committed.
/// If false the completion host will determine if and where the commit character is inserted into the document.
/// True if the changes include the typed character that caused the <see cref="CompletionItem"/>
/// to be committed. If false the completion host will determine if and where the commit
/// character is inserted into the document.
/// </summary>
public bool IncludesCommitCharacter { get; }
private CompletionChange(ImmutableArray<TextChange> textChanges, int? newPosition, bool includesCommit)
private CompletionChange(ImmutableArray<TextChange> textChanges, int? newPosition, bool includesCommitCharacter)
: this(textChanges.Single(), newPosition, includesCommitCharacter)
{
this.TextChanges = textChanges.IsDefault ? ImmutableArray<TextChange>.Empty : textChanges;
this.NewPosition = newPosition;
this.IncludesCommitCharacter = includesCommit;
}
private CompletionChange(TextChange textChange, int? newPosition, bool includesCommitCharacter)
{
TextChange = textChange;
#pragma warning disable CS0618 // Type or member is obsolete
TextChanges = ImmutableArray.Create(textChange);
#pragma warning restore CS0618 // Type or member is obsolete
NewPosition = newPosition;
IncludesCommitCharacter = includesCommitCharacter;
}
/// <summary>
......@@ -44,25 +58,45 @@ private CompletionChange(ImmutableArray<TextChange> textChanges, int? newPositio
/// <param name="includesCommitCharacter">True if the changes include the typed character that caused the <see cref="CompletionItem"/> to be committed.
/// If false, the completion host will determine if and where the commit character is inserted into the document.</param>
/// <returns></returns>
public static CompletionChange Create(ImmutableArray<TextChange> textChanges, int? newPosition = null, bool includesCommitCharacter = false)
[Obsolete("Use Create overload that only takes a single TextChange")]
public static CompletionChange Create(
ImmutableArray<TextChange> textChanges,
int? newPosition,
bool includesCommitCharacter)
{
return new CompletionChange(textChanges, newPosition, includesCommitCharacter);
}
#pragma warning disable RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads.
public static CompletionChange Create(
#pragma warning restore RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads.
TextChange textChange,
int? newPosition = null,
bool includesCommitCharacter = false)
{
return new CompletionChange(textChange, newPosition, includesCommitCharacter);
}
/// <summary>
/// Creates a copy of this <see cref="CompletionChange"/> with the <see cref="TextChange"/> property changed.
/// </summary>
[Obsolete("Use WithTextChange instead")]
public CompletionChange WithTextChanges(ImmutableArray<TextChange> textChanges)
{
return new CompletionChange(textChanges, this.NewPosition, this.IncludesCommitCharacter);
}
public CompletionChange WithTextChange(TextChange textChange)
{
return new CompletionChange(textChange, this.NewPosition, this.IncludesCommitCharacter);
}
/// <summary>
/// Creates a copy of this <see cref="CompletionChange"/> with the <see cref="NewPosition"/> property changed.
/// </summary>
public CompletionChange WithNewPosition(int? newPostion)
{
return new CompletionChange(this.TextChanges, newPostion, this.IncludesCommitCharacter);
return new CompletionChange(this.TextChange, newPostion, this.IncludesCommitCharacter);
}
/// <summary>
......@@ -70,7 +104,7 @@ public CompletionChange WithNewPosition(int? newPostion)
/// </summary>
public CompletionChange WithIncludesCommitCharacter(bool includesCommitCharacter)
{
return new CompletionChange(this.TextChanges, this.NewPosition, includesCommitCharacter);
return new CompletionChange(this.TextChange, this.NewPosition, includesCommitCharacter);
}
}
}
\ No newline at end of file
......@@ -210,6 +210,16 @@ public CompletionItemRules WithFilterCharacterRules(ImmutableArray<CharacterSetM
return this.With(filterRules: filterCharacterRules);
}
internal CompletionItemRules WithFilterCharacterRule(CharacterSetModificationRule rule)
{
return this.With(filterRules: ImmutableArray.Create(rule));
}
internal CompletionItemRules WithCommitCharacterRule(CharacterSetModificationRule rule)
{
return this.With(commitRules: ImmutableArray.Create(rule));
}
/// <summary>
/// Creates a copy of this <see cref="CompletionItemRules"/> with the <see cref="CommitCharacterRules"/> property changed.
/// </summary>
......
......@@ -54,7 +54,7 @@ public virtual Task<CompletionDescription> GetDescriptionAsync(Document document
/// <param name="cancellationToken"></param>
public virtual Task<CompletionChange> GetChangeAsync(Document document, CompletionItem item, char? commitKey, CancellationToken cancellationToken)
{
return Task.FromResult(CompletionChange.Create(ImmutableArray.Create(new TextChange(item.Span, item.DisplayText))));
return Task.FromResult(CompletionChange.Create(new TextChange(item.Span, item.DisplayText)));
}
/// <summary>
......
......@@ -49,10 +49,10 @@ public virtual CompletionRules GetRules()
/// This API uses SourceText instead of Document so implementations can only be based on text, not syntax or semantics.
/// </remarks>
public virtual bool ShouldTriggerCompletion(
SourceText text,
int caretPosition,
CompletionTrigger trigger,
ImmutableHashSet<string> roles = null,
SourceText text,
int caretPosition,
CompletionTrigger trigger,
ImmutableHashSet<string> roles = null,
OptionSet options = null)
{
return false;
......@@ -93,8 +93,8 @@ public virtual TextSpan GetDefaultItemSpan(SourceText text, int caretPosition)
/// <param name="item">The item to get the description for.</param>
/// <param name="cancellationToken"></param>
public virtual Task<CompletionDescription> GetDescriptionAsync(
Document document,
CompletionItem item,
Document document,
CompletionItem item,
CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(CompletionDescription.Empty);
......@@ -110,12 +110,12 @@ public virtual TextSpan GetDefaultItemSpan(SourceText text, int caretPosition)
/// This value is null when the commit was caused by the [TAB] or [ENTER] keys.</param>
/// <param name="cancellationToken"></param>
public virtual Task<CompletionChange> GetChangeAsync(
Document document,
CompletionItem item,
char? commitCharacter = null,
Document document,
CompletionItem item,
char? commitCharacter = null,
CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(CompletionChange.Create(ImmutableArray.Create(new TextChange(item.Span, item.DisplayText))));
return Task.FromResult(CompletionChange.Create(new TextChange(item.Span, item.DisplayText)));
}
}
}
}
\ No newline at end of file
......@@ -452,7 +452,7 @@ internal virtual bool SupportsTriggerOnDeletion(OptionSet options)
}
else
{
return CompletionChange.Create(ImmutableArray.Create(new TextChange(item.Span, item.DisplayText)));
return CompletionChange.Create(new TextChange(item.Span, item.DisplayText));
}
}
......
......@@ -8,6 +8,7 @@
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
using System.Collections.Immutable;
using System.Diagnostics;
namespace Microsoft.CodeAnalysis.Completion.Providers
{
......@@ -83,7 +84,9 @@ public override async Task ProvideCompletionsAsync(CompletionContext context)
return;
}
var items = await GetItemsWorkerAsync(context.Document, context.Position, context.DefaultItemSpan, context.Trigger, context.CancellationToken).ConfigureAwait(false);
var items = await GetItemsWorkerAsync(
context.Document, context.Position, context.DefaultItemSpan,
context.Trigger, context.CancellationToken).ConfigureAwait(false);
if (items != null)
{
context.AddItems(items);
......@@ -212,7 +215,9 @@ public override async Task<CompletionChange> GetChangeAsync(Document document, C
replacementText += afterCaretText;
return CompletionChange.Create(ImmutableArray.Create(new TextChange(replacementSpan, replacementText)), newPosition, includesCommitCharacter: true);
return CompletionChange.Create(
new TextChange(replacementSpan, replacementText),
newPosition, includesCommitCharacter: true);
}
private TextSpan ComputeReplacementSpan(CompletionItem completionItem, SourceText text)
......
......@@ -9,7 +9,6 @@
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
using System;
namespace Microsoft.CodeAnalysis.Completion.Providers
{
......@@ -105,21 +104,9 @@ protected virtual CompletionItem CreateItem(RecommendedKeyword keyword, TextSpan
return set;
}
public override async Task<TextChange?> GetTextChangeAsync(Document document, CompletionItem item, char? ch, CancellationToken cancellationToken)
public override Task<TextChange?> GetTextChangeAsync(Document document, CompletionItem item, char? ch, CancellationToken cancellationToken)
{
var insertionText = item.DisplayText;
if (ch == ' ')
{
var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
var textTypedSoFar = text.GetSubText(item.Span).ToString();
if (textTypedSoFar.Length > 0 && insertionText.StartsWith(textTypedSoFar, StringComparison.OrdinalIgnoreCase))
{
insertionText = insertionText.Substring(0, textTypedSoFar.Length - 1);
}
}
return new TextChange(item.Span, insertionText);
return Task.FromResult((TextChange?)new TextChange(item.Span, item.DisplayText));
}
}
}
}
\ No newline at end of file
......@@ -11,6 +11,7 @@
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
using System.Collections.Immutable;
using System.Collections.Generic;
namespace Microsoft.CodeAnalysis.Completion.Providers
{
......@@ -57,14 +58,42 @@ public override async Task<CompletionChange> GetChangeAsync(Document document, C
}
var changes = await newDocument.GetTextChangesAsync(document, cancellationToken).ConfigureAwait(false);
return CompletionChange.Create(ImmutableArray.CreateRange(changes), newPosition, includesCommitCharacter: true);
var change = Collapse(newText, changes.ToList());
return CompletionChange.Create(change, newPosition, includesCommitCharacter: true);
}
private TextChange Collapse(SourceText newText, List<TextChange> changes)
{
if (changes.Count == 0)
{
return new TextChange(new TextSpan(0, 0), "");
}
else if (changes.Count == 1)
{
return changes[0];
}
// The span we want to replace goes from the start of the first span to the end of
// the last span.
var totalOldSpan = TextSpan.FromBounds(changes.First().Span.Start, changes.Last().Span.End);
// We figure out the text we're replacing with by actually just figuring out the
// new span in the newText and grabbing the text out of that. The newSpan will
// start from the same position as the oldSpan, but it's length will be the old
// span's length + all the deltas we accumulate through each text change. i.e.
// if the first change adds 2 characters and the second change adds 4, then
// the newSpan will be 2+4=6 characters longer than the old span.
var sumOfDeltas = changes.Sum(c => c.NewText.Length - c.Span.Length);
var totalNewSpan = new TextSpan(totalOldSpan.Start, totalOldSpan.Length + sumOfDeltas);
return new TextChange(totalOldSpan, newText.ToString(totalNewSpan));
}
private async Task<Document> DetermineNewDocumentAsync(CompletionItem completionItem, SourceText sourceText, CancellationToken cancellationToken)
{
// The span we're going to replace
var line = sourceText.Lines[MemberInsertionCompletionItem.GetLine(completionItem)];
//var line = textSnapshot.GetLineFromLineNumber(MemberInsertionCompletionItem.GetLine(completionItem));
//var sourceText = textSnapshot.AsText();
var document = sourceText.GetOpenDocumentInCurrentContextWithChanges();
......@@ -210,22 +239,23 @@ private TextSpan ComputeDestinationSpan(SyntaxNode insertionRoot, string inserti
private static readonly ImmutableArray<CharacterSetModificationRule> s_commitRules = ImmutableArray.Create(
CharacterSetModificationRule.Create(CharacterSetModificationKind.Replace, '('));
private static readonly ImmutableArray<CharacterSetModificationRule> s_filterRules = ImmutableArray.Create(
CharacterSetModificationRule.Create(CharacterSetModificationKind.Remove, '('));
private static readonly CompletionItemRules s_defaultRules =
CompletionItemRules.Create(commitCharacterRules: s_commitRules, enterKeyRule: EnterKeyRule.Never);
CompletionItemRules.Create(
commitCharacterRules: s_commitRules,
filterCharacterRules: s_filterRules,
enterKeyRule: EnterKeyRule.Never);
internal virtual CompletionItemRules GetRules()
{
return s_defaultRules;
}
public override Task<TextChange?> GetTextChangeAsync(Document document, CompletionItem selectedItem, char? ch, CancellationToken cancellationToken)
{
return Task.FromResult<TextChange?>(null);
}
public override Task<CompletionDescription> GetDescriptionAsync(Document document, CompletionItem item, CancellationToken cancellationToken)
{
return MemberInsertionCompletionItem.GetDescriptionAsync(item, document, cancellationToken);
}
}
}
}
\ No newline at end of file
......@@ -116,4 +116,4 @@ protected ITypeSymbol GetReturnType(ISymbol symbol)
}
}
}
}
}
\ No newline at end of file
......@@ -8,9 +8,11 @@ Microsoft.CodeAnalysis.Completion.CharacterSetModificationRule.Kind.get -> Micro
Microsoft.CodeAnalysis.Completion.CompletionChange
Microsoft.CodeAnalysis.Completion.CompletionChange.IncludesCommitCharacter.get -> bool
Microsoft.CodeAnalysis.Completion.CompletionChange.NewPosition.get -> int?
Microsoft.CodeAnalysis.Completion.CompletionChange.TextChange.get -> Microsoft.CodeAnalysis.Text.TextChange
Microsoft.CodeAnalysis.Completion.CompletionChange.TextChanges.get -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Text.TextChange>
Microsoft.CodeAnalysis.Completion.CompletionChange.WithIncludesCommitCharacter(bool includesCommitCharacter) -> Microsoft.CodeAnalysis.Completion.CompletionChange
Microsoft.CodeAnalysis.Completion.CompletionChange.WithNewPosition(int? newPostion) -> Microsoft.CodeAnalysis.Completion.CompletionChange
Microsoft.CodeAnalysis.Completion.CompletionChange.WithTextChange(Microsoft.CodeAnalysis.Text.TextChange textChange) -> Microsoft.CodeAnalysis.Completion.CompletionChange
Microsoft.CodeAnalysis.Completion.CompletionChange.WithTextChanges(System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Text.TextChange> textChanges) -> Microsoft.CodeAnalysis.Completion.CompletionChange
Microsoft.CodeAnalysis.Completion.CompletionContext
Microsoft.CodeAnalysis.Completion.CompletionContext.AddItem(Microsoft.CodeAnalysis.Completion.CompletionItem item) -> void
......@@ -197,7 +199,8 @@ override Microsoft.CodeAnalysis.Completion.CompletionServiceWithProviders.Should
override Microsoft.CodeAnalysis.TaggedText.ToString() -> string
static Microsoft.CodeAnalysis.Completion.CharacterSetModificationRule.Create(Microsoft.CodeAnalysis.Completion.CharacterSetModificationKind kind, System.Collections.Immutable.ImmutableArray<char> characters) -> Microsoft.CodeAnalysis.Completion.CharacterSetModificationRule
static Microsoft.CodeAnalysis.Completion.CharacterSetModificationRule.Create(Microsoft.CodeAnalysis.Completion.CharacterSetModificationKind kind, params char[] characters) -> Microsoft.CodeAnalysis.Completion.CharacterSetModificationRule
static Microsoft.CodeAnalysis.Completion.CompletionChange.Create(System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Text.TextChange> textChanges, int? newPosition = null, bool includesCommitCharacter = false) -> Microsoft.CodeAnalysis.Completion.CompletionChange
static Microsoft.CodeAnalysis.Completion.CompletionChange.Create(Microsoft.CodeAnalysis.Text.TextChange textChange, int? newPosition = null, bool includesCommitCharacter = false) -> Microsoft.CodeAnalysis.Completion.CompletionChange
static Microsoft.CodeAnalysis.Completion.CompletionChange.Create(System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Text.TextChange> textChanges, int? newPosition, bool includesCommitCharacter) -> Microsoft.CodeAnalysis.Completion.CompletionChange
static Microsoft.CodeAnalysis.Completion.CompletionDescription.Create(System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.TaggedText> taggedParts) -> Microsoft.CodeAnalysis.Completion.CompletionDescription
static Microsoft.CodeAnalysis.Completion.CompletionDescription.FromText(string text) -> Microsoft.CodeAnalysis.Completion.CompletionDescription
static Microsoft.CodeAnalysis.Completion.CompletionItem.Create(string displayText, string filterText = null, string sortText = null, Microsoft.CodeAnalysis.Text.TextSpan span = default(Microsoft.CodeAnalysis.Text.TextSpan), System.Collections.Immutable.ImmutableDictionary<string, string> properties = null, System.Collections.Immutable.ImmutableArray<string> tags = default(System.Collections.Immutable.ImmutableArray<string>), Microsoft.CodeAnalysis.Completion.CompletionItemRules rules = null) -> Microsoft.CodeAnalysis.Completion.CompletionItem
......
......@@ -97,15 +97,7 @@ private async Task<string> GetInsertionTextAsync(Document document, CompletionIt
var service = CompletionService.GetService(document);
var change = await service.GetChangeAsync(document, item, null, cancellationToken).ConfigureAwait(false);
// normally the items that produce multiple changes are not expecting to trigger the behaviors that rely on looking at the text
if (change.TextChanges.Length == 1)
{
return change.TextChanges[0].NewText;
}
else
{
return item.DisplayText;
}
return change.TextChange.NewText;
}
private SpellCheckCodeAction CreateCodeAction(TSimpleName nameNode, string oldName, string newName, Document document)
......
......@@ -127,7 +127,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers
Private Function GetOfText(symbol As ISymbol, typedChar As Char) As String
If symbol.Kind = SymbolKind.NamedType Then
If typedChar = "("c Then
Return "("
Return ""
Else
Return "(Of"
End If
......@@ -148,6 +148,5 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers
Return GetInsertionText(name, symbol, context.IsRightOfNameSeparator, DirectCast(context, VisualBasicSyntaxContext).WithinAsyncMethod, ch)
End Function
End Module
End Namespace
End Namespace
\ No newline at end of file
......@@ -6,6 +6,7 @@ Imports Microsoft.CodeAnalysis.Completion.Providers
Imports Microsoft.CodeAnalysis.Options
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Extensions.ContextQuery
Imports Microsoft.CodeAnalysis.Completion
Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers
Friend Class KeywordCompletionProvider
......@@ -170,6 +171,5 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers
New KeywordRecommenders.Types.BuiltInTypesKeywordRecommender()
}.ToImmutableArray()
End Function
End Class
End Namespace
End Namespace
\ No newline at end of file
......@@ -75,10 +75,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers
descriptionPosition:=position,
contextPosition:=position,
isArgumentName:=True,
rules:=CompletionItemRules.Default))
rules:=s_itemRules))
Next
End Function
' Typing : or = should not filter the list, but they should commit the list.
Private Shared s_itemRules As CompletionItemRules = CompletionItemRules.Default.
WithFilterCharacterRule(CharacterSetModificationRule.Create(CharacterSetModificationKind.Remove, ":"c, "="c)).
WithCommitCharacterRule(CharacterSetModificationRule.Create(CharacterSetModificationKind.Add, ":"c, "="c))
Public Overrides Function GetDescriptionAsync(document As Document, item As CompletionItem, cancellationToken As CancellationToken) As Task(Of CompletionDescription)
Return SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken)
End Function
......@@ -199,7 +204,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers
argumentList = Nothing
End Sub
Public Overrides Function GetTextChangeAsync(document As Document, selectedItem As CompletionItem, ch As Char?, cancellationToken As CancellationToken) As Task(Of TextChange?)
Protected Overrides Function GetTextChangeAsync(selectedItem As CompletionItem, ch As Char?, cancellationToken As CancellationToken) As Task(Of TextChange?)
Dim symbolItem = selectedItem
Dim insertionText = SymbolCompletionItem.GetInsertionText(selectedItem)
Dim change As TextChange
......@@ -212,6 +217,5 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers
End If
Return Task.FromResult(Of TextChange?)(change)
End Function
End Class
End Namespace
End Namespace
\ No newline at end of file
......@@ -142,10 +142,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers
Return Not s.DeclaringSyntaxReferences.Select(Function(r) r.GetSyntax()).All(Function(a) a.Span.IntersectsWith(token.Span))
End Function
Public Overrides Function GetTextChangeAsync(document As Document, selectedItem As CompletionItem, ch As Char?, cancellationToken As CancellationToken) As Task(Of TextChange?)
Protected Overrides Function GetTextChangeAsync(selectedItem As CompletionItem, ch As Char?, cancellationToken As CancellationToken) As Task(Of TextChange?)
Dim insertionText = SymbolCompletionItem.GetInsertionText(selectedItem)
Return Task.FromResult(Of TextChange?)(New TextChange(selectedItem.Span, insertionText))
End Function
End Class
End Namespace
End Namespace
\ No newline at end of file
......@@ -110,7 +110,7 @@ private ImmutableArray<CompletionItem> GetItems(SourceText text, Document docume
return helper.GetItems(pathThroughLastSlash, documentPath: null);
}
public override Task<TextChange?> GetTextChangeAsync(Document document, CompletionItem selectedItem, char? ch, CancellationToken cancellationToken)
protected override Task<TextChange?> GetTextChangeAsync(CompletionItem selectedItem, char? ch, CancellationToken cancellationToken)
{
// When we commit "\\" when the user types \ we have to adjust for the fact that the
// controller will automatically append \ after we commit. Because of that, we don't
......@@ -121,7 +121,7 @@ private ImmutableArray<CompletionItem> GetItems(SourceText text, Document docume
return Task.FromResult<TextChange?>(new TextChange(selectedItem.Span, "\\"));
}
return base.GetTextChangeAsync(document, selectedItem, ch, cancellationToken);
return base.GetTextChangeAsync(selectedItem, ch, cancellationToken);
}
}
}
}
\ No newline at end of file
......@@ -78,7 +78,11 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Snippets
options.GetOption(CompletionOptions.TriggerOnTypingLetters, LanguageNames.VisualBasic)
End Function
Public Sub Commit(completionItem As CompletionItem, textView As ITextView, subjectBuffer As ITextBuffer, triggerSnapshot As ITextSnapshot, commitChar As Char?) Implements ICustomCommitCompletionProvider.Commit
Public Sub Commit(completionItem As CompletionItem,
textView As ITextView,
subjectBuffer As ITextBuffer,
triggerSnapshot As ITextSnapshot,
commitChar As Char?) Implements ICustomCommitCompletionProvider.Commit
Dim snippetClient = SnippetExpansionClient.GetSnippetExpansionClient(textView, subjectBuffer, _editorAdaptersFactoryService)
Dim trackingSpan = triggerSnapshot.CreateTrackingSpan(completionItem.Span.ToSpan(), SpanTrackingMode.EdgeInclusive)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册