diff options
author | Todd Grunke <toddgrun@microsoft.com> | 2018-03-02 19:21:30 +0300 |
---|---|---|
committer | Todd Grunke <toddgrun@microsoft.com> | 2018-03-02 19:21:30 +0300 |
commit | 402ba17a3e650315d74662196f7f06b59cabf432 (patch) | |
tree | 0089920fd237bf1cc9d8a6236800d2093f0d2a8e /src | |
parent | 5500d83af009a076fb993b68f5a276f355e61799 (diff) |
Update nuget version to 19
Updte editor nuget to 15.7.111-preview-g28b775fb96
Diffstat (limited to 'src')
9 files changed, 201 insertions, 106 deletions
diff --git a/src/Language/Impl/Language/Completion/AsyncCompletionBroker.cs b/src/Language/Impl/Language/Completion/AsyncCompletionBroker.cs index 440c3a3..d38044c 100644 --- a/src/Language/Impl/Language/Completion/AsyncCompletionBroker.cs +++ b/src/Language/Impl/Language/Completion/AsyncCompletionBroker.cs @@ -28,9 +28,6 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation internal IGuardedOperations GuardedOperations { get; set; } [Import] - internal ITextStructureNavigatorSelectorService TextStructureNavigatorSelectorService { get; set; } - - [Import] private JoinableTaskContext JoinableTaskContext; [Import] @@ -69,7 +66,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation session.TextView.Properties.RemoveProperty(typeof(IAsyncCompletionSession)); } - IAsyncCompletionSession IAsyncCompletionBroker.TriggerCompletion(ITextView view, SnapshotSpan applicableSpan) + IAsyncCompletionSession IAsyncCompletionBroker.TriggerCompletion(ITextView view, SnapshotPoint triggerLocation, SnapshotSpan applicableSpan) { var session = GetSession(view); if (session != null) @@ -77,16 +74,23 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation return session; } - // TODO: Need to map applicableSpan to respective buffers - var sourcesWithLocations = CompletionUtilities.GetCompletionSourcesWithMappedSpans(view, applicableSpan, GetCompletionItemSourceProviders); + var sourcesWithLocations = CompletionUtilities.GetCompletionSourcesWithMappedPoints(view, triggerLocation, GetCompletionItemSourceProviders); if (!sourcesWithLocations.Any()) { // There is no completion source available for this buffer return null; } - // TODO: use applicableSpan - var buffers = CompletionUtilities.GetBuffersForSpan(view, applicableSpan).ToImmutableArray(); + var potentialCommitCharsBuilder = ImmutableArray.CreateBuilder<char>(); + foreach (var source in sourcesWithLocations.Keys) + { + // Unfortunately we can't use for loop here because IDictionary.Keys is ICollection which can't be accessed with an indexer + GuardedOperations.CallExtensionPoint( + errorSource: source, + call: () => potentialCommitCharsBuilder.AddRange(source.GetPotentialCommitCharacters())); + } + + var buffers = CompletionUtilities.GetBuffersForPoint(view, triggerLocation).ToImmutableArray(); var service = buffers .Select(b => GetCompletionService(b.ContentType)) .FirstOrDefault(s => s != null) @@ -109,7 +113,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation } var telemetry = GetOrCreateTelemetry(view); - session = new AsyncCompletionSession(applicableSpan, JoinableTaskContext.Factory, uiFactory, sourcesWithLocations, service, this, view, telemetry); + session = new AsyncCompletionSession(applicableSpan, potentialCommitCharsBuilder.ToImmutable(), JoinableTaskContext.Factory, uiFactory, sourcesWithLocations, service, this, view, telemetry); view.Properties.AddProperty(typeof(IAsyncCompletionSession), session); view.Closed += TextView_Closed; @@ -269,9 +273,9 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation } } - internal string GetItemSourceName(IAsyncCompletionItemSource source) => OrderedCompletionItemSourceProviders.FirstOrDefault(n => n.Value == source)?.Metadata.Name ?? string.Empty; - internal string GetCompletionServiceName(IAsyncCompletionService service) => OrderedCompletionServiceProviders.FirstOrDefault(n => n.Value == service)?.Metadata.Name ?? string.Empty; - internal string GetCompletionPresenterProviderName(ICompletionPresenterProvider provider) => OrderedPresenterProviders.FirstOrDefault(n => n.Value == provider)?.Metadata.Name ?? string.Empty; + internal string GetItemSourceName(IAsyncCompletionItemSource source) => OrderedCompletionItemSourceProviders.FirstOrDefault(n => n.IsValueCreated && n.Value == source)?.Metadata.Name ?? string.Empty; + internal string GetCompletionServiceName(IAsyncCompletionService service) => OrderedCompletionServiceProviders.FirstOrDefault(n => n.IsValueCreated && n.Value == service)?.Metadata.Name ?? string.Empty; + internal string GetCompletionPresenterProviderName(ICompletionPresenterProvider provider) => OrderedPresenterProviders.FirstOrDefault(n => n.IsValueCreated && n.Value == provider)?.Metadata.Name ?? string.Empty; // Parity with legacy telemetry private void EmulateLegacyCompletionTelemetry(IContentType contentType, ITextView textView) diff --git a/src/Language/Impl/Language/Completion/AsyncCompletionSession.cs b/src/Language/Impl/Language/Completion/AsyncCompletionSession.cs index a0dd3ca..b7a49ba 100644 --- a/src/Language/Impl/Language/Completion/AsyncCompletionSession.cs +++ b/src/Language/Impl/Language/Completion/AsyncCompletionSession.cs @@ -22,15 +22,15 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation internal class AsyncCompletionSession : IAsyncCompletionSession, ICompletionComputationCallbackHandler<CompletionModel> { // Available data and services - private readonly IDictionary<IAsyncCompletionItemSource, SnapshotSpan> _completionSources; + private readonly IDictionary<IAsyncCompletionItemSource, SnapshotPoint> _completionSources; private readonly IAsyncCompletionService _completionService; private readonly SnapshotSpan _initialApplicableSpan; private readonly JoinableTaskFactory _jtf; private readonly ICompletionPresenterProvider _presenterProvider; private readonly AsyncCompletionBroker _broker; private readonly ITextView _view; - private readonly ITextStructureNavigator _textStructureNavigator; private readonly IGuardedOperations _guardedOperations; + private readonly ImmutableArray<char> _potentialCommitChars; // Presentation: ICompletionPresenter _gui; // Must be accessed from GUI thread @@ -67,18 +67,18 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation public bool IsDismissed => _isDismissed; - public AsyncCompletionSession(SnapshotSpan applicableSpan, JoinableTaskFactory jtf, ICompletionPresenterProvider presenterProvider, - IDictionary<IAsyncCompletionItemSource, SnapshotSpan> completionSources, + public AsyncCompletionSession(SnapshotSpan applicableSpan, ImmutableArray<char> potentialCommitChars, JoinableTaskFactory jtf, + ICompletionPresenterProvider presenterProvider, IDictionary<IAsyncCompletionItemSource, SnapshotPoint> completionSources, IAsyncCompletionService completionService, AsyncCompletionBroker broker, ITextView view, CompletionTelemetryHost telemetryHost) { _initialApplicableSpan = applicableSpan; + _potentialCommitChars = potentialCommitChars; _jtf = jtf; _presenterProvider = presenterProvider; _broker = broker; _completionSources = completionSources; _completionService = completionService; _view = view; - _textStructureNavigator = broker.TextStructureNavigatorSelectorService?.GetTextStructureNavigator(view.TextBuffer); _guardedOperations = broker.GuardedOperations; _telemetry = new CompletionSessionTelemetry(telemetryHost, completionService, presenterProvider); PageStepSize = presenterProvider?.ResultsPerPage ?? 1; @@ -87,6 +87,9 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation bool IAsyncCompletionSession.CommitIfUnique(CancellationToken token) { + if (_isDismissed) + return false; + // Note that this will deadlock if OpenOrUpdate wasn't called ahead of time. var lastModel = _computation.WaitAndGetResult(); if (lastModel.UniqueItem != null) @@ -133,7 +136,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation // Commit the suggestion mode item return Commit(typedChar, lastModel.SuggestionModeItem, token); } - else if (!lastModel.PresentedItems.Any()) + else if (lastModel.PresentedItems.IsDefaultOrEmpty) { // There is nothing to commit Dismiss(); @@ -149,6 +152,9 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation private CustomCommitBehavior Commit(char typedChar, CompletionItem itemToCommit, CancellationToken token) { CustomCommitBehavior result = CustomCommitBehavior.None; + if (_isDismissed) + return result; + var lastModel = _computation.WaitAndGetResult(); if (!_jtf.Context.IsOnMainThread) @@ -195,8 +201,8 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation _broker.DismissSession(this); _guardedOperations.RaiseEvent(this, Dismissed); _view.Caret.PositionChanged -= OnCaretPositionChanged; - _computationCancellation.Cancel(); _computation = null; + _computationCancellation.Cancel(); if (_gui != null) { @@ -218,6 +224,9 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation void IAsyncCompletionSession.OpenOrUpdate(ITextView view, CompletionTrigger trigger, SnapshotPoint triggerLocation) { + if (_isDismissed) + return; + if (_computation == null) { _computation = new ModelComputation<CompletionModel>(PrioritizedTaskScheduler.AboveNormalInstance, _computationCancellation.Token, _guardedOperations, this); @@ -230,6 +239,9 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation internal void InvokeAndCommitIfUnique(ITextView view, CompletionTrigger trigger, SnapshotPoint triggerLocation, CancellationToken token) { + if (_isDismissed) + return; + ((IAsyncCompletionSession)this).OpenOrUpdate(view, trigger, triggerLocation); if (((IAsyncCompletionSession)this).CommitIfUnique(token)) { @@ -305,18 +317,17 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation Dismiss(); } + /// <summary> + /// Determines whether the commit code path should be taken. Since this method is on a typing hot path, + /// we return quickly if the character is not found in the predefined list of potential commit characters. + /// </summary> + /// <remarks>This method runs on UI thread. ShouldCommit performs mapping which requires UI thread.</remarks> bool IAsyncCompletionSession.ShouldCommit(ITextView view, char typeChar, SnapshotPoint triggerLocation) { - foreach (var contentType in CompletionUtilities.GetBuffersForPoint(view, triggerLocation).Select(b => b.ContentType)) + if (_potentialCommitChars.Contains(typeChar)) { - if (_broker.TryGetKnownCommitCharacters(contentType, view, out var commitChars)) - { - if (commitChars.Contains(typeChar)) - { - if (ShouldCommit(view, typeChar, triggerLocation)) - return true; - } - } + if (ShouldCommit(view, typeChar, triggerLocation)) + return true; } return false; } @@ -392,7 +403,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation _completionSources.Select( p => _guardedOperations.CallExtensionPointAsync<CompletionContext>( errorSource: p.Key, - asyncCall: () => p.Key.GetCompletionContextAsync(trigger, p.Value, token), + asyncCall: () => p.Key.GetCompletionContextAsync(trigger, triggerLocation, _initialApplicableSpan, token), valueOnThrow: null ))); var initialCompletionItems = nestedResults.Where(n => n != null && !n.Items.IsDefaultOrEmpty).SelectMany(n => n.Items).ToImmutableArray(); @@ -467,17 +478,29 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation // Always record keystrokes, even if filtering is preempted _telemetry.RecordKeystroke(); - // Filtering got preempted, so store the most recent snapshot for the next time we filter - if (token.IsCancellationRequested || thisId != _lastFilteringTaskId) - return model.WithSnapshot(triggerLocation.Snapshot); + // Completion got cancelled + if (token.IsCancellationRequested || model == null) + return default(CompletionModel); // Dismiss if we are outside of the applicable span - // TODO: dismiss if applicable span was reduced to zero AND moved again (e.g. first backspace keeps completion open, second backspace closes it) - if (triggerLocation < model.ApplicableSpan.GetStartPoint(triggerLocation.Snapshot) || triggerLocation > model.ApplicableSpan.GetEndPoint(triggerLocation.Snapshot)) + var currentlyApplicableSpan = model.ApplicableSpan.GetSpan(triggerLocation.Snapshot); + if (triggerLocation < currentlyApplicableSpan.Start + || triggerLocation > currentlyApplicableSpan.End) + { + ((IAsyncCompletionSession)this).Dismiss(); + return model; + } + // Record the first time the span is empty. If it is empty the second time we're here, and user is deleting, then dismiss + if (currentlyApplicableSpan.IsEmpty && model.ApplicableSpanWasEmpty && trigger.Reason == CompletionTriggerReason.Deletion) { ((IAsyncCompletionSession)this).Dismiss(); return model; } + model = model.WithApplicableSpanEmptyRecord(currentlyApplicableSpan.IsEmpty); + + // Filtering got preempted, so store the most recent snapshot for the next time we filter + if (thisId != _lastFilteringTaskId) + return model.WithSnapshot(triggerLocation.Snapshot); ComputationStopwatch.Restart(); @@ -515,7 +538,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation model = model.WithSoftSelection(false); // Prepare the suggestionModeItem if we ever change the mode - var enteredText = model.ApplicableSpan.GetText(triggerLocation.Snapshot); + var enteredText = currentlyApplicableSpan.GetText(); var suggestionModeItem = new CompletionItem(enteredText, SuggestionModeCompletionItemSource.Instance); _guardedOperations.RaiseEvent(this, ItemsUpdated, new CompletionItemsWithHighlightEventArgs(returnedItems)); @@ -654,6 +677,9 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation /// </summary> public bool ShouldCommit(ITextView view, char typeChar, SnapshotPoint triggerLocation) { + if (!_jtf.Context.IsOnMainThread) + throw new InvalidOperationException($"This method must be callled on the UI thread."); + var mappingPoint = view.BufferGraph.CreateMappingPoint(triggerLocation, PointTrackingMode.Negative); return _completionSources .Select(p => (p, mappingPoint.GetPoint(p.Value.Snapshot.TextBuffer, PositionAffinity.Predecessor))) diff --git a/src/Language/Impl/Language/Completion/CompletionCommandHandlers.cs b/src/Language/Impl/Language/Completion/CompletionCommandHandlers.cs index 7451582..2e14382 100644 --- a/src/Language/Impl/Language/Completion/CompletionCommandHandlers.cs +++ b/src/Language/Impl/Language/Completion/CompletionCommandHandlers.cs @@ -1,6 +1,7 @@ using System; using System.ComponentModel.Composition; using Microsoft.VisualStudio.Commanding; +using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Editor.Commanding.Commands; using Microsoft.VisualStudio.Text.Operations; @@ -120,7 +121,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation var applicableSpan = Broker.ShouldTriggerCompletion(args.TextView, default(char), location); if (applicableSpan.HasValue) { - var session = Broker.TriggerCompletion(args.TextView, applicableSpan.Value); + var session = Broker.TriggerCompletion(args.TextView, location, applicableSpan.Value); session?.OpenOrUpdate(args.TextView, trigger, location); return true; } @@ -137,7 +138,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation var applicableSpan = Broker.ShouldTriggerCompletion(args.TextView, default(char), location); if (applicableSpan.HasValue) { - var session = Broker.TriggerCompletion(args.TextView, applicableSpan.Value) as AsyncCompletionSession; + var session = Broker.TriggerCompletion(args.TextView, location, applicableSpan.Value) as AsyncCompletionSession; session?.InvokeAndCommitIfUnique(args.TextView, trigger, location, executionContext.OperationContext.UserCancellationToken); return true; } @@ -252,19 +253,30 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation void IChainedCommandHandler<TypeCharCommandArgs>.ExecuteCommand(TypeCharCommandArgs args, Action nextCommandHandler, CommandExecutionContext executionContext) { var initialTextSnapshot = args.TextView.TextSnapshot; + var view = args.TextView; - // Execute other commands in the chain to see the change in the buffer. - nextCommandHandler(); - - // We are only inteterested in the top buffer. Currently, commanding implementation calls us multiple times, once per each buffer. - if (args.TextView.BufferGraph.TopBuffer != args.SubjectBuffer) + if (args.TextView.TextBuffer != args.SubjectBuffer) + { + // We are only inteterested in the top buffer. Currently, commanding implementation calls us multiple times, once per each buffer. + // Allow other command handlers to act. + nextCommandHandler(); return; + } - var view = args.TextView; var location = view.Caret.Position.BufferPosition; + // Call ShouldCommit before nextCommandHandler so that extenders + // get the same view of the buffer in both ShouldCommit and Commit var sessionToCommit = Broker.GetSession(args.TextView); - if (sessionToCommit?.ShouldCommit(view, args.TypedChar, location) == true) + var shouldCommit = sessionToCommit?.ShouldCommit(view, args.TypedChar, location); + + if (shouldCommit == true) { + // Execute other commands in the chain to see the change in the buffer. + nextCommandHandler(); + + // Buffer has changed, update the snapshot + location = view.Caret.Position.BufferPosition; + using (var undoTransaction = new CaretPreservingEditTransaction("Completion", view, UndoHistoryRegistry, EditorOperationsFactoryService)) { UndoUtilities.RollbackToBeforeTypeChar(initialTextSnapshot, args.SubjectBuffer); @@ -272,15 +284,20 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation var customBehavior = sessionToCommit.Commit(executionContext.OperationContext.UserCancellationToken, args.TypedChar); - if ((customBehavior & CustomCommitBehavior.SurpressFurtherCommandHandlers) == 0) + if ((customBehavior & CustomCommitBehavior.SuppressFurtherCommandHandlers) == 0) nextCommandHandler(); // Replay the key, so that we get brace completion. // Complete the transaction before stopping it. undoTransaction.Complete(); } - // Snapshot has changed when committing. Update it for when we try to trigger new session. - location = view.Caret.Position.BufferPosition; } + else + { + nextCommandHandler(); + } + + // Buffer might have changed. Update it for when we try to trigger new session. + location = view.Caret.Position.BufferPosition; var trigger = new CompletionTrigger(CompletionTriggerReason.Insertion, args.TypedChar); var session = Broker.GetSession(args.TextView); @@ -293,7 +310,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation var applicableSpan = Broker.ShouldTriggerCompletion(view, args.TypedChar, location); if (applicableSpan.HasValue) { - var newSession = Broker.TriggerCompletion(view, applicableSpan.Value); + var newSession = Broker.TriggerCompletion(view, location, applicableSpan.Value); newSession?.OpenOrUpdate(view, trigger, location); } } diff --git a/src/Language/Impl/Language/Completion/CompletionModel.cs b/src/Language/Impl/Language/Completion/CompletionModel.cs index 111b580..84d18f3 100644 --- a/src/Language/Impl/Language/Completion/CompletionModel.cs +++ b/src/Language/Impl/Language/Completion/CompletionModel.cs @@ -85,6 +85,12 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation public readonly CompletionItem UniqueItem; /// <summary> + /// When completion starts, its <see cref="ApplicableSpan"/> may be empty. Initially, don't dismiss. + /// Further, the span is empty if user removes characters. We would like to dismiss then. + /// </summary> + public readonly bool ApplicableSpanWasEmpty; + + /// <summary> /// Constructor for the initial model /// </summary> public CompletionModel(ImmutableArray<CompletionItem> initialItems, ImmutableArray<CompletionItem> sortedItems, @@ -104,6 +110,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation SuggestionModeDescription = suggestionModeDescription; SuggestionModeItem = suggestionModeItem; UniqueItem = null; + ApplicableSpanWasEmpty = false; } /// <summary> @@ -111,7 +118,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation /// </summary> private CompletionModel(ImmutableArray<CompletionItem> initialItems, ImmutableArray<CompletionItem> sortedItems, ITrackingSpan applicableSpan, CompletionTriggerReason initialTriggerReason, ITextSnapshot snapshot, ImmutableArray<CompletionFilterWithState> filters, ImmutableArray<CompletionItemWithHighlight> presentedItems, bool useSoftSelection, bool useSuggestionMode, - string suggestionModeDescription, int selectedIndex, bool selectSuggestionMode, CompletionItem suggestionModeItem, CompletionItem uniqueItem) + string suggestionModeDescription, int selectedIndex, bool selectSuggestionMode, CompletionItem suggestionModeItem, CompletionItem uniqueItem, bool applicableSpanWasEmpty) { InitialItems = initialItems; SortedItems = sortedItems; @@ -126,6 +133,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation SelectSuggestionMode = selectSuggestionMode; SuggestionModeDescription = suggestionModeDescription; SuggestionModeItem = suggestionModeItem; + ApplicableSpanWasEmpty = applicableSpanWasEmpty; } public CompletionModel WithPresentedItems(ImmutableArray<CompletionItemWithHighlight> newPresentedItems, int newSelectedIndex) @@ -144,7 +152,8 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation selectedIndex: newSelectedIndex, // Updated selectSuggestionMode: SelectSuggestionMode, suggestionModeItem: SuggestionModeItem, - uniqueItem: UniqueItem + uniqueItem: UniqueItem, + applicableSpanWasEmpty: ApplicableSpanWasEmpty ); } @@ -164,7 +173,8 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation selectedIndex: SelectedIndex, selectSuggestionMode: SelectSuggestionMode, suggestionModeItem: SuggestionModeItem, - uniqueItem: UniqueItem + uniqueItem: UniqueItem, + applicableSpanWasEmpty: ApplicableSpanWasEmpty ); } @@ -184,7 +194,8 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation selectedIndex: SelectedIndex, selectSuggestionMode: SelectSuggestionMode, suggestionModeItem: SuggestionModeItem, - uniqueItem: UniqueItem + uniqueItem: UniqueItem, + applicableSpanWasEmpty: ApplicableSpanWasEmpty ); } @@ -204,7 +215,8 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation selectedIndex: newIndex, // Updated selectSuggestionMode: false, // Explicit selection of regular item suggestionModeItem: SuggestionModeItem, - uniqueItem: UniqueItem + uniqueItem: UniqueItem, + applicableSpanWasEmpty: ApplicableSpanWasEmpty ); } @@ -224,7 +236,8 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation selectedIndex: -1, // Deselect regular item selectSuggestionMode: true, // Explicit selection of suggestion item suggestionModeItem: SuggestionModeItem, - uniqueItem: UniqueItem + uniqueItem: UniqueItem, + applicableSpanWasEmpty: ApplicableSpanWasEmpty ); } @@ -244,7 +257,8 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation selectedIndex: SelectedIndex, selectSuggestionMode: SelectSuggestionMode, suggestionModeItem: SuggestionModeItem, - uniqueItem: UniqueItem + uniqueItem: UniqueItem, + applicableSpanWasEmpty: ApplicableSpanWasEmpty ); } @@ -267,7 +281,8 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation selectedIndex: SelectedIndex, selectSuggestionMode: SelectSuggestionMode, suggestionModeItem: newSuggestionModeItem, - uniqueItem: UniqueItem + uniqueItem: UniqueItem, + applicableSpanWasEmpty: ApplicableSpanWasEmpty ); } @@ -291,7 +306,8 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation selectedIndex: SelectedIndex, selectSuggestionMode: SelectSuggestionMode, suggestionModeItem: SuggestionModeItem, - uniqueItem: newUniqueItem + uniqueItem: newUniqueItem, + applicableSpanWasEmpty: ApplicableSpanWasEmpty ); } @@ -311,7 +327,8 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation selectedIndex: SelectedIndex, selectSuggestionMode: SelectSuggestionMode, suggestionModeItem: SuggestionModeItem, - uniqueItem: UniqueItem + uniqueItem: UniqueItem, + applicableSpanWasEmpty: ApplicableSpanWasEmpty ); } @@ -331,7 +348,29 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation selectedIndex: selectedIndex, // Updated selectSuggestionMode: SelectSuggestionMode, suggestionModeItem: suggestionModeItem, // Updated - uniqueItem: uniqueItem // Updated + uniqueItem: uniqueItem, // Updated + applicableSpanWasEmpty: ApplicableSpanWasEmpty + ); + } + + internal CompletionModel WithApplicableSpanEmptyRecord(bool applicableSpanIsEmpty) + { + return new CompletionModel( + initialItems: InitialItems, + sortedItems: SortedItems, + applicableSpan: ApplicableSpan, + initialTriggerReason: InitialTriggerReason, + snapshot: Snapshot, + filters: Filters, + presentedItems: PresentedItems, + useSoftSelection: UseSoftSelection, + useSuggestionMode: DisplaySuggestionMode, + suggestionModeDescription: SuggestionModeDescription, + selectedIndex: SelectedIndex, + selectSuggestionMode: SelectSuggestionMode, + suggestionModeItem: SuggestionModeItem, + uniqueItem: UniqueItem, + applicableSpanWasEmpty: applicableSpanIsEmpty // Updated ); } } diff --git a/src/Language/Impl/Language/Completion/CompletionUtilities.cs b/src/Language/Impl/Language/Completion/CompletionUtilities.cs index c1da8e3..0703cb6 100644 --- a/src/Language/Impl/Language/Completion/CompletionUtilities.cs +++ b/src/Language/Impl/Language/Completion/CompletionUtilities.cs @@ -100,45 +100,6 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation return pointsInBuffers; } - internal static IDictionary<IAsyncCompletionItemSource, SnapshotSpan> GetCompletionSourcesWithMappedSpans(ITextView textView, SnapshotSpan originalSpan, Func<IContentType, ImmutableArray<IAsyncCompletionItemSourceProvider>> completionItemSourceProviders) - { - // See comment in GetCompletionSourcesWithMappedPoints for explanation - - var sortedContentTypes = new SortedSet<IContentType>(ContentTypeComparer.Instance); - var result = new Dictionary<IAsyncCompletionItemSource, SnapshotSpan>(); - - var mappedSpans = GetSpansOnAvailableBuffers(textView, originalSpan); - foreach (var mappedSpan in mappedSpans) - { - AddContentTypeHierarchy(sortedContentTypes, mappedSpan.Snapshot.ContentType); - } - - foreach (var contentType in sortedContentTypes) - { - foreach (var mappedSpan in mappedSpans) - { - if (mappedSpan.Snapshot.ContentType.IsOfType(contentType.TypeName)) - { - foreach (var sourceProvider in completionItemSourceProviders(contentType)) - { - var source = sourceProvider.GetOrCreate(textView); - if (!result.ContainsKey(source)) - result.Add(source, mappedSpan); - } - } - } - } - return result; - } - - private static IEnumerable<SnapshotSpan> GetSpansOnAvailableBuffers(ITextView textView, SnapshotSpan span) - { - var mappingSpan = textView.BufferGraph.CreateMappingSpan(span, SpanTrackingMode.EdgePositive); - var buffers = textView.BufferGraph.GetTextBuffers(b => mappingSpan.GetSpans(b).Any()); - var spansInBuffers = buffers.Select(b => mappingSpan.GetSpans(b).First()); // TODO: What if there is more than one span? - return spansInBuffers; - } - private static void AddContentTypeHierarchy(SortedSet<IContentType> sortedContentTypes, IContentType contentType) { sortedContentTypes.Add(contentType); diff --git a/src/Language/Impl/Language/Completion/DefaultCompletionService.cs b/src/Language/Impl/Language/Completion/DefaultCompletionService.cs index 563eb4d..50f3728 100644 --- a/src/Language/Impl/Language/Completion/DefaultCompletionService.cs +++ b/src/Language/Impl/Language/Completion/DefaultCompletionService.cs @@ -161,7 +161,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation return CustomCommitBehavior.None; } - async Task<CompletionContext> IAsyncCompletionItemSource.GetCompletionContextAsync(CompletionTrigger trigger, SnapshotSpan triggerLocation, CancellationToken token) + async Task<CompletionContext> IAsyncCompletionItemSource.GetCompletionContextAsync(CompletionTrigger trigger, SnapshotPoint triggerLocation, SnapshotSpan triggerLocation, CancellationToken token) { return await Task.FromResult(new CompletionContext( ImmutableArray.Create( @@ -193,7 +193,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation SnapshotSpan? IAsyncCompletionItemSource.ShouldTriggerCompletion(char typeChar, SnapshotPoint triggerLocation) { var charBeforeCaret = triggerLocation.Subtract(1).GetChar(); - if (commitCharacters.Contains(charBeforeCaret)) + if (commitCharacters.Contains(charBeforeCaret) || triggerLocation.Position == 0) { // skip the typed character. the applicable span starts at the caret return new SnapshotSpan(triggerLocation, 0); @@ -206,6 +206,9 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation } } +#endif +#if DEBUG && true + [Export(typeof(IAsyncCompletionItemSourceProvider))] [Name("Debug HTML completion item source")] [Order(After = "default")] @@ -231,7 +234,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation return CustomCommitBehavior.None; } - async Task<CompletionContext> IAsyncCompletionItemSource.GetCompletionContextAsync(CompletionTrigger trigger, SnapshotSpan applicableSpan, CancellationToken token) + async Task<CompletionContext> IAsyncCompletionItemSource.GetCompletionContextAsync(CompletionTrigger trigger, SnapshotPoint triggerLocation, SnapshotSpan applicableSpan, CancellationToken token) { return await Task.FromResult(new CompletionContext(ImmutableArray.Create(new CompletionItem("html", this), new CompletionItem("head", this), new CompletionItem("body", this), new CompletionItem("header", this)))); } @@ -254,7 +257,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation SnapshotSpan? IAsyncCompletionItemSource.ShouldTriggerCompletion(char typeChar, SnapshotPoint triggerLocation) { var charBeforeCaret = triggerLocation.Subtract(1).GetChar(); - if (commitCharacters.Contains(charBeforeCaret)) + if (commitCharacters.Contains(charBeforeCaret) || triggerLocation.Position == 0) { // skip the typed character. the applicable span starts at the caret return new SnapshotSpan(triggerLocation, 0); diff --git a/src/Language/Impl/Language/Completion/ImportBucket.cs b/src/Language/Impl/Language/Completion/ImportBucket.cs new file mode 100644 index 0000000..ba631d0 --- /dev/null +++ b/src/Language/Impl/Language/Completion/ImportBucket.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.VisualStudio.Text.Utilities; + +namespace Microsoft.VisualStudio.Language.Intellisense.Implementation +{ + /// <summary> + /// Lightweight stack-like view over a readonly ordered list of command handlers. + /// </summary> + internal class ImportBucket<T> + { + private int _currentImportSetting; + private readonly IReadOnlyList<Lazy<T, IOrderableContentTypeMetadata>> _imports; + + public ImportBucket(IReadOnlyList<Lazy<T, IOrderableContentTypeMetadata>> imports) + { + _imports = imports ?? throw new ArgumentNullException(nameof(imports)); + } + + public bool IsEmpty => _currentImportSetting >= _imports.Count; + + public Lazy<T, IOrderableContentTypeMetadata> Peek() + { + if (!IsEmpty) + { + return _imports[_currentImportSetting]; + } + + throw new InvalidOperationException($"{nameof(ImportBucket<T>)} is empty."); + } + + internal Lazy<T, IOrderableContentTypeMetadata> Pop() + { + if (!IsEmpty) + { + return _imports[_currentImportSetting++]; + } + + throw new InvalidOperationException($"{nameof(ImportBucket<T>)} is empty."); + } + } +} diff --git a/src/Language/Impl/Language/Completion/SuggestionModeCompletionItemSource.cs b/src/Language/Impl/Language/Completion/SuggestionModeCompletionItemSource.cs index dc5b31e..a5b34e4 100644 --- a/src/Language/Impl/Language/Completion/SuggestionModeCompletionItemSource.cs +++ b/src/Language/Impl/Language/Completion/SuggestionModeCompletionItemSource.cs @@ -28,7 +28,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation return CustomCommitBehavior.None; } - Task<CompletionContext> IAsyncCompletionItemSource.GetCompletionContextAsync(CompletionTrigger trigger, SnapshotSpan applicableSpan, CancellationToken token) + Task<CompletionContext> IAsyncCompletionItemSource.GetCompletionContextAsync(CompletionTrigger trigger, SnapshotPoint triggerLocation, SnapshotSpan applicableSpan, CancellationToken token) { throw new NotImplementedException("This item source is not meant to be registered. It is used only to provide tooltip."); } diff --git a/src/Microsoft.VisualStudio.Text.Implementation.csproj b/src/Microsoft.VisualStudio.Text.Implementation.csproj index 13cdb29..2b7377c 100644 --- a/src/Microsoft.VisualStudio.Text.Implementation.csproj +++ b/src/Microsoft.VisualStudio.Text.Implementation.csproj @@ -5,10 +5,10 @@ <SignAssembly>true</SignAssembly> <AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile> <DelaySign>false</DelaySign> - <Version>15.0.18-pre</Version> + <Version>15.0.19-pre</Version> <AssemblyVersion>15.0.0.0</AssemblyVersion> - <NuGetVersionEditor>15.7.95-preview-gf6780a6ebe</NuGetVersionEditor> - <NuGetVersionLanguage>15.7.95-preview-gf6780a6ebe</NuGetVersionLanguage> + <NuGetVersionEditor>15.7.111-preview-g28b775fb96</NuGetVersionEditor> + <NuGetVersionLanguage>15.7.111-preview-g28b775fb96</NuGetVersionLanguage> </PropertyGroup> <PropertyGroup> |