提交 26927781 编写于 作者: C CyrusNajmabadi 提交者: GitHub

Merge pull request #12275 from CyrusNajmabadi/completionRazorMaster

Refactor completion commit.
......@@ -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));
}
}
}
......
......@@ -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);
}
}
}
......
......@@ -248,7 +248,7 @@ private void CommitItem(PresentationItem item)
return;
}
this.Commit(item, this.sessionOpt.Computation.InitialUnfilteredModel, commitChar: null);
this.CommitOnNonTypeChar(item, this.sessionOpt.Computation.InitialUnfilteredModel);
}
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;
}
......
......@@ -174,7 +174,7 @@ private void CommitOnTab(out bool committed)
return;
}
Commit(model.SelectedItem, model, commitChar: null);
CommitOnNonTypeChar(model.SelectedItem, model);
committed = true;
}
}
......
......@@ -29,17 +29,39 @@ void ICommandHandler<TypeCharCommandArgs>.ExecuteCommand(TypeCharCommandArgs arg
AssertIsForeground();
var initialCaretPosition = GetCaretPointInViewBuffer();
// When a character is typed it is *always* sent through to the editor. This way the
// editor always represents what would have been typed had completion not been involved
// at this point. After we send the character into the buffer we then decide what to do
// with the completion set. If we decide to commit it then we will replace the
// appropriate span (which will include the character just sent to the buffer) with the
// appropriate insertion text *and* the character typed. This way, after we commit, the
// editor has the insertion text of the selected item, and the character typed. It
// also means that if we then undo that we'll see the text that would have been typed
// had no completion been active.
// at this point. That means that if we decide to commit, then undo'ing the commit will
// return you to the code that you would have typed if completion was not up.
//
// The steps we follow for commit are as follows:
//
// 1) send the commit character through to the buffer.
// 2) open a transaction.
// 2a) roll back the text to before the text was sent through
// 2b) commit the item.
// 2c) send the commit character through again.*
// 2d) commit the transaction.
//
// 2c is very important. it makes sure that post our commit all our normal features
// run depending on what got typed. For example if the commit character was (
// then brace completion may run. If it was ; then formatting may run. But, importantly
// this code doesn't need to know anything about that. Furthermore, because that code
// runs within this transaction, then the user can always undo and get to what the code
// would have been if completion was not involved.
//
// 2c*: note sending the commit character through to the buffer again can be controlled
// by the completion item. For example, completion items that want to totally handle
// what gets output into the buffer can ask for this not to happen. An example of this
// is override completion. If the user types "override Method(" then we'll want to
// spit out the entire method and *not* also spit out "(" again.
// In order to support 2a (rolling back), we capture hte state of the buffer before
// we send the character through. We then just apply the edits in reverse order to
// roll us back.
var initialTextSnapshot = this.SubjectBuffer.CurrentSnapshot;
var initialCaretPosition = GetCaretPointInViewBuffer();
// Note: while we're doing this, we don't want to hear about buffer changes (since we
// know they're going to happen). So we disconnect and reconnect to the event
......@@ -83,7 +105,7 @@ 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, nextHandler);
return;
}
else if (_autoBraceCompletionChars.Contains(args.TypedChar) &&
......@@ -94,7 +116,7 @@ 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, nextHandler);
return;
}
else
......@@ -221,7 +243,7 @@ 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, nextHandler);
}
else
{
......@@ -322,23 +344,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)
......@@ -362,6 +379,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;
}
......@@ -382,28 +405,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)
......@@ -427,10 +446,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);
......@@ -439,7 +464,8 @@ private string GetCurrentFilterText(Model model, CompletionItem selectedItem)
return filterText;
}
private void CommitOnTypeChar(char ch)
private void CommitOnTypeChar(
char ch, ITextSnapshot initialTextSnapshot, Action nextHandler)
{
AssertIsForeground();
......@@ -451,7 +477,9 @@ 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, nextHandler);
}
}
}
\ No newline at end of file
......@@ -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
......@@ -16,7 +16,9 @@ internal class Model
{
private readonly DisconnectedBufferGraph _disconnectedBufferGraph;
public ITextSnapshot TriggerSnapshot { get { return _disconnectedBufferGraph.SubjectBufferSnapshot; } }
public ITextSnapshot ViewTriggerSnapshot { get { return _disconnectedBufferGraph.ViewSnapshot; } }
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
......@@ -154,6 +159,7 @@ internal class Model
var selectedPresentationItem = totalItems.FirstOrDefault(it => it.Item == selectedItem);
return new Model(
triggerDocument,
disconnectedBufferGraph,
originalList,
totalItems,
......@@ -195,86 +201,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<string> filterText = default(Optional<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 newFilterText = filterText.HasValue ? filterText.Value : FilterText;
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 &&
newFilterText == FilterText &&
newIsHardSelection == IsHardSelection &&
newIsUnique == IsUnique &&
newUseSuggestionMode == UseSuggestionMode &&
newSuggestionModeItem == SuggestionModeItem &&
newCommitTrackingSpanEndPoint == CommitTrackingSpanEndPoint)
{
return this;
}
return new Model(
TriggerDocument, _disconnectedBufferGraph, OriginalList, TotalItems, newFilteredItems,
newSelectedItem, CompletionItemFilters, newFilterState, newFilterText,
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, FilterText, 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, FilterText, 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, FilterText, 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, FilterText, 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, FilterText, 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, FilterText, 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, FilterText, 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, FilterText, IsHardSelection, IsUnique,
UseSuggestionMode, SuggestionModeItem, DefaultSuggestionModeItem, Trigger,
CommitTrackingSpanEndPoint, DismissIfEmpty);
return With(filterState: filterState);
}
internal Model WithFilterText(string filterText)
{
return new Model(_disconnectedBufferGraph, OriginalList, TotalItems, FilteredItems,
SelectedItem, CompletionItemFilters, FilterState, filterText, IsHardSelection, IsUnique,
UseSuggestionMode, SuggestionModeItem, DefaultSuggestionModeItem, Trigger,
CommitTrackingSpanEndPoint, DismissIfEmpty);
return With(filterText: filterText);
}
internal SnapshotSpan GetCurrentSpanInSnapshot(ViewTextSpan originalSpan, ITextSnapshot textSnapshot)
......@@ -324,4 +334,4 @@ internal ViewTextSpan GetViewBufferSpan(TextSpan subjectBufferSpan)
return _disconnectedBufferGraph.GetSubjectBufferTextSpanInViewBuffer(subjectBufferSpan);
}
}
}
}
\ No newline at end of file
......@@ -331,7 +331,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;
......@@ -340,7 +341,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);
......@@ -369,7 +370,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;
......@@ -420,7 +422,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);
......@@ -789,12 +792,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");
}
}
}
......
......@@ -1583,9 +1583,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
......
......@@ -27,7 +27,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
......
......@@ -224,7 +224,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));
}
......
......@@ -337,7 +337,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;
......@@ -125,4 +122,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
......@@ -9,15 +9,13 @@
using Microsoft.CodeAnalysis.Completion.Providers;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Recommendations;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
using Microsoft.CodeAnalysis.LanguageServices;
using System.Linq;
using Microsoft.CodeAnalysis.Shared.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.Completion.Providers
{
......@@ -109,19 +107,20 @@ protected override async Task<AbstractSyntaxContext> CreateContext(Document docu
private static CompletionItemRules s_importDirectiveRules =
CompletionItemRules.Create(commitCharacterRules: ImmutableArray.Create(CharacterSetModificationRule.Create(CharacterSetModificationKind.Replace, '.', ';')));
private static CompletionItemRules s_importDirectiveRules_Preselect = s_importDirectiveRules.WithSelectionBehavior(CompletionItemSelectionBehavior.HardSelection);
private static CompletionItemRules s_PreselectionRules = CompletionItemRules.Default.WithSelectionBehavior(CompletionItemSelectionBehavior.HardSelection);
private static CompletionItemRules s_importDirectiveRules_preselect = s_importDirectiveRules.WithSelectionBehavior(CompletionItemSelectionBehavior.HardSelection);
// '<' 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, '<'));
private static readonly CompletionItemRules s_itemRules_preselect = s_itemRules.WithSelectionBehavior(CompletionItemSelectionBehavior.HardSelection);
protected override CompletionItemRules GetCompletionItemRules(List<ISymbol> symbols, AbstractSyntaxContext context, bool preselect)
{
if (context.IsInImportsDirective)
{
return preselect ? s_importDirectiveRules_Preselect : s_importDirectiveRules;
}
else
{
return preselect ? s_PreselectionRules : CompletionItemRules.Default;
}
return context.IsInImportsDirective
? preselect ? s_importDirectiveRules_preselect : s_importDirectiveRules
: preselect ? s_itemRules_preselect : s_itemRules;
}
protected override CompletionItemRules GetCompletionItemRules(IReadOnlyList<ISymbol> symbols, AbstractSyntaxContext context)
......@@ -130,12 +129,6 @@ protected override CompletionItemRules GetCompletionItemRules(IReadOnlyList<ISym
throw new NotImplementedException();
}
protected override CompletionItemSelectionBehavior PreselectedItemSelectionBehavior
{
get
{
return CompletionItemSelectionBehavior.HardSelection;
}
}
protected override CompletionItemSelectionBehavior PreselectedItemSelectionBehavior => CompletionItemSelectionBehavior.HardSelection;
}
}
}
\ 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,10 +45,15 @@ 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);
}
......
// 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,47 @@ 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 = null,
bool includesCommitCharacter = false)
{
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.
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
public static CompletionChange Create(
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
#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 +106,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
......@@ -5,6 +5,7 @@
using System.Collections.Immutable;
using System.Globalization;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.Text;
......
......@@ -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>
......
......@@ -55,7 +55,7 @@ public virtual Task<CompletionDescription> GetDescriptionAsync(Document document
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>
......
......@@ -125,7 +125,7 @@ public virtual TextSpan GetDefaultCompletionListSpan(SourceText text, int caretP
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)));
}
/// <summary>
......
......@@ -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
{
......@@ -85,6 +86,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext context)
var items = await GetItemsWorkerAsync(
context.Document, context.Position, context.CompletionListSpan, context.Trigger, context.CancellationToken).ConfigureAwait(false);
if (items != null)
{
context.AddItems(items);
......@@ -207,7 +209,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
{
......@@ -104,24 +103,11 @@ protected virtual CompletionItem CreateItem(RecommendedKeyword keyword)
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 currentSnapshot = document.Project.Solution.Workspace.CurrentSolution.GetDocument(document.Id);
var text = await currentSnapshot.GetTextAsync(cancellationToken).ConfigureAwait(false);
var textTypedSoFar = text.GetSubText(GetCurrentSpan(item.Span, text)).ToString() + ch;
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));
}
internal abstract TextSpan GetCurrentSpan(TextSpan span, SourceText text);
}
}
}
\ 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
*REMOVED*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
Microsoft.CodeAnalysis.Completion.CompletionContext.CompletionListSpan.get -> Microsoft.CodeAnalysis.Text.TextSpan
Microsoft.CodeAnalysis.Completion.CompletionContext.CompletionListSpan.set -> void
Microsoft.CodeAnalysis.Completion.CompletionChange.TextChange.get -> Microsoft.CodeAnalysis.Text.TextChange
Microsoft.CodeAnalysis.Completion.CompletionChange.WithTextChange(Microsoft.CodeAnalysis.Text.TextChange textChange) -> Microsoft.CodeAnalysis.Completion.CompletionChange
Microsoft.CodeAnalysis.Completion.CompletionItemRules.SelectionBehavior.get -> Microsoft.CodeAnalysis.Completion.CompletionItemSelectionBehavior
Microsoft.CodeAnalysis.Completion.CompletionItemRules.WithSelectionBehavior(Microsoft.CodeAnalysis.Completion.CompletionItemSelectionBehavior selectionBehavior) -> Microsoft.CodeAnalysis.Completion.CompletionItemRules
Microsoft.CodeAnalysis.Completion.CompletionItemSelectionBehavior
......@@ -16,6 +18,7 @@ Microsoft.CodeAnalysis.Completion.SnippetsRule.AlwaysInclude = 2 -> Microsoft.Co
Microsoft.CodeAnalysis.Completion.SnippetsRule.Default = 0 -> Microsoft.CodeAnalysis.Completion.SnippetsRule
Microsoft.CodeAnalysis.Completion.SnippetsRule.IncludeAfterTypingIdentifierQuestionTab = 3 -> Microsoft.CodeAnalysis.Completion.SnippetsRule
Microsoft.CodeAnalysis.Completion.SnippetsRule.NeverInclude = 1 -> Microsoft.CodeAnalysis.Completion.SnippetsRule
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.CompletionItem.Create(string displayText, string filterText = null, string sortText = null, 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
static Microsoft.CodeAnalysis.Completion.CompletionItem.Create(string displayText, string filterText, string sortText, Microsoft.CodeAnalysis.Text.TextSpan span, System.Collections.Immutable.ImmutableDictionary<string, string> properties, System.Collections.Immutable.ImmutableArray<string> tags, Microsoft.CodeAnalysis.Completion.CompletionItemRules rules) -> Microsoft.CodeAnalysis.Completion.CompletionItem
static Microsoft.CodeAnalysis.Completion.CompletionItemRules.Create(System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Completion.CharacterSetModificationRule> filterCharacterRules = default(System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Completion.CharacterSetModificationRule>), System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Completion.CharacterSetModificationRule> commitCharacterRules = default(System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Completion.CharacterSetModificationRule>), Microsoft.CodeAnalysis.Completion.EnterKeyRule enterKeyRule = Microsoft.CodeAnalysis.Completion.EnterKeyRule.Default, bool formatOnCommit = false, int? matchPriority = null, Microsoft.CodeAnalysis.Completion.CompletionItemSelectionBehavior selectionBehavior = Microsoft.CodeAnalysis.Completion.CompletionItemSelectionBehavior.Default) -> Microsoft.CodeAnalysis.Completion.CompletionItemRules
......
......@@ -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
......@@ -174,6 +175,5 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers
Friend Overrides Function GetCurrentSpan(span As TextSpan, text As SourceText) As TextSpan
Return CompletionUtilities.GetCompletionItemSpan(text, span.End)
End Function
End Class
End Namespace
End Namespace
\ No newline at end of file
......@@ -74,10 +74,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
......@@ -198,7 +203,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
......@@ -211,6 +216,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
......@@ -140,10 +140,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
......@@ -6,14 +6,12 @@ Imports Microsoft.CodeAnalysis.Completion
Imports Microsoft.CodeAnalysis.Completion.Providers
Imports Microsoft.CodeAnalysis.LanguageServices
Imports Microsoft.CodeAnalysis.Options
Imports Microsoft.CodeAnalysis.Recommendations
Imports Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Extensions.ContextQuery
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers
Partial Friend Class SymbolCompletionProvider
Inherits AbstractRecommendationServiceBasedCompletionProvider
......@@ -85,15 +83,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers
CompletionItemRules.Create(commitCharacterRules:=ImmutableArray.Create(CharacterSetModificationRule.Create(CharacterSetModificationKind.Replace, "."c)))
Private Shared s_importDirectiveRules_preselect As CompletionItemRules =
s_importDirectiveRules.WithSelectionBehavior(CompletionItemSelectionBehavior.SoftSelection)
Private Shared s_preselectedCompletionItemRules As CompletionItemRules =
CompletionItemRules.Default.WithSelectionBehavior(CompletionItemSelectionBehavior.SoftSelection)
' '(' should not filter the completion list, even though it's in generic items like IList(Of...)
Private Shared ReadOnly s_itemRules As CompletionItemRules = CompletionItemRules.Default.
WithFilterCharacterRule(CharacterSetModificationRule.Create(CharacterSetModificationKind.Remove, "("c)).
WithCommitCharacterRule(CharacterSetModificationRule.Create(CharacterSetModificationKind.Add, "("c))
Private Shared ReadOnly s_itemRules_preselect As CompletionItemRules = s_itemRules.WithSelectionBehavior(CompletionItemSelectionBehavior.SoftSelection)
Protected Overrides Function GetCompletionItemRules(symbols As List(Of ISymbol), context As AbstractSyntaxContext, preselect As Boolean) As CompletionItemRules
If context.IsInImportsDirective Then
Return If(preselect, s_importDirectiveRules_preselect, s_importDirectiveRules)
Else
Return If(preselect, s_preselectedCompletionItemRules, CompletionItemRules.Default)
End If
Return If(context.IsInImportsDirective,
If(preselect, s_importDirectiveRules_preselect, s_importDirectiveRules),
If(preselect, s_itemRules_preselect, s_itemRules))
End Function
Protected Overrides Function GetCompletionItemRules(symbols As IReadOnlyList(Of ISymbol), context As AbstractSyntaxContext) As CompletionItemRules
......@@ -110,6 +111,5 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers
Return CompletionItemSelectionBehavior.SoftSelection
End Get
End Property
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
......@@ -71,7 +71,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.
先完成此消息的编辑!
想要评论请 注册