Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/microsoft/vs-editor-api.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKirill Osenkov <github@osenkov.com>2018-02-14 23:54:42 +0300
committerKirill Osenkov <github@osenkov.com>2018-02-14 23:54:42 +0300
commit8f4dbae806287ce5ecf847c3178faad2f6b0bcd9 (patch)
treed8f5918f34a330e7f3d94bcc3e806ab099382c95 /src
parent4ec55c3dc69faf56a9840c621512523390a2575b (diff)
Update to VS-Platform source as of 3c4ef6e5fcb2717a52d5ba74d1cd1dfe64d16342.
Diffstat (limited to 'src')
-rw-r--r--src/Language/Impl/Language/Completion/AsyncCompletionBroker.cs217
-rw-r--r--src/Language/Impl/Language/Completion/AsyncCompletionSession.cs448
-rw-r--r--src/Language/Impl/Language/Completion/CompletionCommandHandlers.cs329
-rw-r--r--src/Language/Impl/Language/Completion/CompletionModel.cs191
-rw-r--r--src/Language/Impl/Language/Completion/CompletionUtilities.cs17
-rw-r--r--src/Language/Impl/Language/Completion/DefaultCompletionService.cs115
-rw-r--r--src/Language/Impl/Language/Completion/ModernCompletionFeature.cs32
-rw-r--r--src/Language/Impl/Language/Completion/SelectDownCommandHandler.cs40
-rw-r--r--src/Language/Impl/Language/Completion/SelectPageDownCommandHandler.cs40
-rw-r--r--src/Language/Impl/Language/Completion/SelectPageUpCommandHandler.cs40
-rw-r--r--src/Language/Impl/Language/Completion/SelectUpCommandHandler.cs40
-rw-r--r--src/Language/Impl/Language/Completion/SuggestionModeCompletionItemSource.cs61
-rw-r--r--src/Language/Impl/Language/Strings.Designer.cs72
-rw-r--r--src/Language/Impl/Language/Strings.resx123
-rw-r--r--src/Microsoft.VisualStudio.Text.Implementation.csproj16
-rw-r--r--src/Text/Def/TextData/Model/IExtensionPerformanceTracker.cs10
-rw-r--r--src/Text/Def/TextUI/Utilities/AbstractUIThreadOperationContext.cs22
-rw-r--r--src/Text/Def/TextUI/Utilities/IUIThreadOperationExecutor.cs16
-rw-r--r--src/Text/Impl/ClassificationAggregator/ClassifierAggregator.cs6
-rw-r--r--src/Text/Impl/Commanding/CommandingStrings.Designer.cs11
-rw-r--r--src/Text/Impl/Commanding/CommandingStrings.resx5
-rw-r--r--src/Text/Impl/Commanding/EditorCommandHandlerService.cs9
-rw-r--r--src/Text/Impl/Outlining/OutliningManager.cs22
-rw-r--r--src/Text/Util/TextDataUtil/GuardedOperations.cs50
24 files changed, 1354 insertions, 578 deletions
diff --git a/src/Language/Impl/Language/Completion/AsyncCompletionBroker.cs b/src/Language/Impl/Language/Completion/AsyncCompletionBroker.cs
index c7c1268..630d1cb 100644
--- a/src/Language/Impl/Language/Completion/AsyncCompletionBroker.cs
+++ b/src/Language/Impl/Language/Completion/AsyncCompletionBroker.cs
@@ -1,20 +1,16 @@
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.ComponentModel.Composition;
using System.Linq;
+using System.Threading;
using System.Threading.Tasks;
-using Microsoft.VisualStudio.Language.Intellisense;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
-using System.Collections.Immutable;
+using Microsoft.VisualStudio.Text.Operations;
+using Microsoft.VisualStudio.Text.Utilities;
using Microsoft.VisualStudio.Threading;
using Microsoft.VisualStudio.Utilities;
-using Microsoft.VisualStudio.Text.Utilities;
-
-#if NET46
-using System.ComponentModel.Composition;
-#else
-using System.Composition;
-#endif
namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
{
@@ -27,27 +23,32 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
[Import(AllowDefault = true)]
internal ILoggingServiceInternal Logger { get; set; }
+ // This may be used to GetExtentOfWord, but it doesn't fully work yet, so we're not using it.
[Import(AllowDefault = true)]
internal ITextDocumentFactoryService TextDocumentFactoryService { get; set; }
[Import]
- private IGuardedOperations GuardedOperation { get; set; }
+ internal IGuardedOperations GuardedOperations { get; set; }
[Import]
private IContentTypeRegistryService ContentTypeRegistryService { get; set; }
+ [Import]
+ internal ITextStructureNavigatorSelectorService TextStructureNavigatorSelectorService { get; set; }
+
[ImportMany]
- private IEnumerable<Lazy<ICompletionUIProvider, IOrderableContentTypeMetadata>> UnorderedUiFactories { get; set; }
+ private IEnumerable<Lazy<ICompletionPresenterProvider, IOrderableContentTypeMetadata>> UnorderedPresenterProviders { get; set; }
+ // TODO: use the provider pattern
[ImportMany]
private IEnumerable<Lazy<IAsyncCompletionItemSource, IOrderableContentTypeMetadata>> UnorderedCompletionItemSources { get; set; }
[ImportMany]
private IEnumerable<Lazy<IAsyncCompletionService, IOrderableContentTypeMetadata>> UnorderedCompletionServices { get; set; }
- private IList<Lazy<ICompletionUIProvider, IOrderableContentTypeMetadata>> _orderedUiFactories;
- internal IList<Lazy<ICompletionUIProvider, IOrderableContentTypeMetadata>> OrderedUiFactories
- => _orderedUiFactories ?? (_orderedUiFactories = Orderer.Order(UnorderedUiFactories));
+ private IList<Lazy<ICompletionPresenterProvider, IOrderableContentTypeMetadata>> _orderedPresenterProviders;
+ internal IList<Lazy<ICompletionPresenterProvider, IOrderableContentTypeMetadata>> OrderedPresenterProviders
+ => _orderedPresenterProviders ?? (_orderedPresenterProviders = Orderer.Order(UnorderedPresenterProviders));
private IList<Lazy<IAsyncCompletionItemSource, IOrderableContentTypeMetadata>> _orderedCompletionItemSources;
internal IList<Lazy<IAsyncCompletionItemSource, IOrderableContentTypeMetadata>> OrderedCompletionItemSources
@@ -57,56 +58,24 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
internal IList<Lazy<IAsyncCompletionService, IOrderableContentTypeMetadata>> OrderedCompletionServices
=> _orderedCompletionServices ?? (_orderedCompletionServices = Orderer.Order(UnorderedCompletionServices));
- private ImmutableDictionary<IContentType, ImmutableArray<string>> _commitCharacters = ImmutableDictionary<IContentType, ImmutableArray<string>>.Empty;
+ private ImmutableDictionary<IContentType, ImmutableSortedSet<char>> _commitCharacters = ImmutableDictionary<IContentType, ImmutableSortedSet<char>>.Empty;
private ImmutableDictionary<IContentType, ImmutableArray<IAsyncCompletionItemSource>> _cachedCompletionItemSources = ImmutableDictionary<IContentType, ImmutableArray<IAsyncCompletionItemSource>>.Empty;
private ImmutableDictionary<IContentType, IAsyncCompletionService> _cachedCompletionServices = ImmutableDictionary<IContentType, IAsyncCompletionService>.Empty;
- private ImmutableDictionary<IContentType, ICompletionUIProvider> _cachedUiFactories = ImmutableDictionary<IContentType, ICompletionUIProvider>.Empty;
- bool firstRun = true; // used only for diagnostics
+ private ImmutableDictionary<IContentType, ICompletionPresenterProvider> _cachedUiFactories = ImmutableDictionary<IContentType, ICompletionPresenterProvider>.Empty;
+ private bool firstRun = true; // used only for diagnostics
private bool _firstInvocationReported; // used for "time to code"
- void IAsyncCompletionBroker.Dismiss(ITextView view)
- {
- var session = GetSession(view);
- if (session == null)
- {
- return;
- }
- view.Properties.RemoveProperty(typeof(IAsyncCompletionSession));
- session.DismissAndHide();
- }
-
- void IAsyncCompletionBroker.Commit(ITextView view, string edit)
- {
- var session = GetSession(view);
- if (session == null)
- {
- return;
- }
-
- session.Commit(edit);
- ((IAsyncCompletionBroker)this).Dismiss(view);
- }
-
- void IAsyncCompletionBroker.OpenOrUpdate(ITextView view, ITrackingSpan trackedEdit, CompletionTrigger trigger, SnapshotPoint triggerLocation)
+ internal void DismissSession(IAsyncCompletionSession session)
{
- var session = GetSession(view);
- if (session == null)
- {
- // Either the session was dismissed, or Begin was not called yet.
- // what would be a good way to tell the two apart?
- // throw new InvalidOperationException("No completion session available for this view. Call Begin first.");
- return;
- }
- session.OpenOrUpdate(view, trackedEdit, trigger, triggerLocation);
+ session.TextView.Properties.RemoveProperty(typeof(IAsyncCompletionSession));
}
- void IAsyncCompletionBroker.TriggerCompletion(ITextView view, SnapshotPoint triggerLocation)
+ IAsyncCompletionSession IAsyncCompletionBroker.TriggerCompletion(ITextView view, SnapshotPoint triggerLocation)
{
var session = GetSession(view);
if (session != null)
{
- // Session already exists.
- return;
+ return session;
}
// TODO: Handle the race condition: two consecutive OpenAsync. Both create completion
@@ -116,7 +85,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
if (!sourcesWithLocations.Any())
{
// There is no completion source available for this buffer
- return;
+ return null;
}
var buffers = CompletionUtilities.GetBuffersForTriggerPoint(view, triggerLocation).ToImmutableArray();
@@ -126,17 +95,17 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
if (service == null)
{
- // There is no modern completion service available for this buffer
- return;
+ // This should never happen because we provide a default and IsCompletionFeatureAvailable would have returned false
+ throw new InvalidOperationException("Default completion service was not found. Completion will be unavailable.");
}
var uiFactory = buffers
- .Select(b => GetUiFactory(b.ContentType))
- .FirstOrDefault(s => s != null);
+ .Select(n => GetUiFactory(n.ContentType))
+ .FirstOrDefault(n => n != null);
if (firstRun)
{
- System.Diagnostics.Debug.Assert(uiFactory != null, $"No instance of {nameof(ICompletionUIProvider)} is loaded. The prototype completion will work without the UI.");
+ System.Diagnostics.Debug.Assert(uiFactory != null, $"No instance of {nameof(ICompletionPresenterProvider)} is loaded. Completion will work without the UI.");
firstRun = false;
}
session = new AsyncCompletionSession(JtContext.Factory, uiFactory, sourcesWithLocations, service, this, view);
@@ -144,7 +113,9 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
view.Closed += TextView_Closed;
// Additionally, emulate the legacy completion telemetry
- emulateLegacyCompletionTelemetry(buffers.FirstOrDefault()?.ContentType, view);
+ EmulateLegacyCompletionTelemetry(buffers.FirstOrDefault()?.ContentType, view);
+
+ return session;
}
private ImmutableArray<IAsyncCompletionItemSource> GetCompletionItemSources(IContentType contentType)
@@ -154,14 +125,15 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
return cachedSources;
}
- ImmutableArray<IAsyncCompletionItemSource> result = ImmutableArray<IAsyncCompletionItemSource>.Empty;
+ var builder = ImmutableArray.CreateBuilder<IAsyncCompletionItemSource>();
foreach (var item in OrderedCompletionItemSources)
{
if (item.Metadata.ContentTypes.Any(n => contentType.IsOfType(n)))
{
- result = result.Add(item.Value);
+ builder.Add(item.Value);
}
}
+ var result = builder.ToImmutable();
_cachedCompletionItemSources = _cachedCompletionItemSources.Add(contentType, result);
return result;
}
@@ -173,7 +145,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
return service;
}
- IAsyncCompletionService bestService = GuardedOperation.InvokeBestMatchingFactory(
+ IAsyncCompletionService bestService = GuardedOperations.InvokeBestMatchingFactory(
providerHandles: OrderedCompletionServices,
getter: n => n,
dataContentType: contentType,
@@ -184,15 +156,15 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
return bestService;
}
- private ICompletionUIProvider GetUiFactory(IContentType contentType)
+ private ICompletionPresenterProvider GetUiFactory(IContentType contentType)
{
if (_cachedUiFactories.TryGetValue(contentType, out var factory))
{
return factory;
}
- ICompletionUIProvider bestFactory = GuardedOperation.InvokeBestMatchingFactory(
- providerHandles: OrderedUiFactories,
+ ICompletionPresenterProvider bestFactory = GuardedOperations.InvokeBestMatchingFactory(
+ providerHandles: OrderedPresenterProviders,
getter: n => n,
dataContentType: contentType,
contentTypeRegistryService: ContentTypeRegistryService,
@@ -202,93 +174,67 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
return bestFactory;
}
- private void TextView_Closed(object sender, EventArgs e)
+ internal bool TryGetKnownCommitCharacters(IContentType contentType, out ImmutableSortedSet<char> commitChars)
{
- ((IAsyncCompletionBroker)this).Dismiss((ITextView)sender);
- try
+ if (_commitCharacters.TryGetValue(contentType, out commitChars))
{
- Task.Run(async () =>
- {
- await Task.WhenAll(OrderedCompletionItemSources
- .Where(n => n.IsValueCreated)
- .Select(n => n.Value)
- .Select(n => n.HandleViewClosedAsync((ITextView)sender)));
- });
+ return commitChars.Any();
}
- catch
+ var allCommitChars = new List<char>();
+ foreach (var source in GetCompletionItemSources(contentType))
{
-
+ GuardedOperations.CallExtensionPoint(
+ errorSource: source,
+ call: () => allCommitChars.AddRange(source.GetPotentialCommitCharacters())
+ );
}
+ commitChars = ImmutableSortedSet.CreateRange(allCommitChars);
+ _commitCharacters = _commitCharacters.Add(contentType, commitChars);
+ return commitChars.Any();
}
- bool IAsyncCompletionBroker.IsCompletionActive(ITextView view)
- {
- return view.Properties.ContainsProperty(typeof(IAsyncCompletionSession));
- }
-
- bool IAsyncCompletionBroker.ShouldCommitCompletion(ITextView view, string edit, SnapshotPoint triggerLocation)
+ private void TextView_Closed(object sender, EventArgs e)
{
- if (!((IAsyncCompletionBroker)this).IsCompletionActive(view))
+ GetSession((ITextView)sender).Dismiss();
+ // TODO: unlink this
+ Task.Run(async () =>
{
- return false;
- }
-
- foreach (var contentType in CompletionUtilities.GetBuffersForTriggerPoint(view, triggerLocation).Select(b => b.ContentType))
- {
- if (TryGetKnownCommitCharacters(contentType, out var commitChars))
- {
- if (commitChars.Contains(edit[0].ToString()))
- {
- if (GetSession(view).ShouldCommit(view, edit, triggerLocation))
- return true;
- }
- }
- }
- return false;
+ await Task.WhenAll(OrderedCompletionItemSources
+ .Where(n => n.IsValueCreated)
+ .Select(n => n.Value)
+ .Select(n => GuardedOperations.CallExtensionPointAsync(n, () => n.HandleViewClosedAsync((ITextView)sender))));
+ });
}
- private bool TryGetKnownCommitCharacters(IContentType contentType, out ImmutableArray<string> commitChars)
+ bool IAsyncCompletionBroker.IsCompletionActive(ITextView view)
{
- if (_commitCharacters.TryGetValue(contentType, out commitChars))
- {
- return commitChars.Any();
- }
- var commitCharsBuilder = ImmutableArray.CreateBuilder<string>();
- foreach (var source in GetCompletionItemSources(contentType))
- {
- commitCharsBuilder.AddRange(source.GetPotentialCommitCharacters());
- }
- commitChars = commitCharsBuilder.Distinct().ToImmutableArray();
- _commitCharacters = _commitCharacters.Add(contentType, commitChars);
- return commitChars.Any();
+ return view.Properties.ContainsProperty(typeof(IAsyncCompletionSession));
}
- bool IAsyncCompletionBroker.ShouldTriggerCompletion(ITextView view, string edit, SnapshotPoint triggerLocation)
+ bool IAsyncCompletionBroker.ShouldTriggerCompletion(ITextView textView, char typeChar, SnapshotPoint triggerLocation)
{
- var sourcesWithLocations = CompletionUtilities.GetCompletionSourcesWithMappedLocations(view, triggerLocation, GetCompletionItemSources);
- return sourcesWithLocations.Any(p => p.Key.ShouldTriggerCompletion(edit, p.Value));
+ var sourcesWithLocations = CompletionUtilities.GetCompletionSourcesWithMappedLocations(textView, triggerLocation, GetCompletionItemSources);
+ return sourcesWithLocations.Any(p => GuardedOperations.CallExtensionPoint(
+ errorSource: p.Key,
+ call: () => p.Key.ShouldTriggerCompletion(typeChar, p.Value),
+ valueOnThrow: false));
}
- private IAsyncCompletionSession GetSession(ITextView view)
+ public IAsyncCompletionSession GetSession(ITextView textView)
{
- if (view.Properties.TryGetProperty(typeof(IAsyncCompletionSession), out IAsyncCompletionSession property))
+ if (textView.Properties.TryGetProperty(typeof(IAsyncCompletionSession), out IAsyncCompletionSession session))
{
- return property;
+ return session;
}
return null;
}
- void IAsyncCompletionBroker.SelectUp(ITextView view) => GetSession(view)?.SelectUp();
- void IAsyncCompletionBroker.SelectDown(ITextView view) => GetSession(view)?.SelectDown();
- void IAsyncCompletionBroker.SelectPageUp(ITextView view) => GetSession(view)?.SelectPageUp();
- void IAsyncCompletionBroker.SelectPageDown(ITextView view) => GetSession(view)?.SelectPageDown();
-
// Helper methods for telemetry
internal string GetItemSourceName(IAsyncCompletionItemSource source) => OrderedCompletionItemSources.FirstOrDefault(n => n.Value == source)?.Metadata.Name ?? string.Empty;
internal string GetCompletionServiceName(IAsyncCompletionService service) => OrderedCompletionServices.FirstOrDefault(n => n.Value == service)?.Metadata.Name ?? string.Empty;
- // Partiy with legacy telemetry
- private void emulateLegacyCompletionTelemetry(IContentType contentType, ITextView view)
+ // Parity with legacy telemetry
+ private void EmulateLegacyCompletionTelemetry(IContentType contentType, ITextView textView)
{
if (Logger == null || _firstInvocationReported)
return;
@@ -307,7 +253,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
}
return null;
}
- var fileExtension = GetFileExtension(view.TextBuffer) ?? "Unknown";
+ var fileExtension = GetFileExtension(textView.TextBuffer) ?? "Unknown";
var reportedContentType = contentType?.ToString() ?? "Unknown";
_firstInvocationReported = true;
@@ -315,5 +261,22 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
("VS.Editor.IntellisenseFirstRun.Opened.ContentType", reportedContentType),
("VS.Editor.IntellisenseFirstRun.Opened.FileExtension", fileExtension));
}
+
+ const string IsCompletionAvailableProperty = "IsCompletionAvailable";
+ bool IAsyncCompletionBroker.IsCompletionSupported(ITextView view)
+ {
+ bool featureIsAvailable;
+ if (view.Properties.TryGetProperty(IsCompletionAvailableProperty, out featureIsAvailable))
+ {
+ return featureIsAvailable;
+ }
+
+ featureIsAvailable = UnorderedCompletionItemSources
+ .Any(n => n.Metadata.ContentTypes.Any(ct => view.TextBuffer.ContentType.IsOfType(ct)));
+ featureIsAvailable &= UnorderedCompletionServices
+ .Any(n => n.Metadata.ContentTypes.Any(ct => view.TextBuffer.ContentType.IsOfType(ct)));
+ view.Properties.AddProperty(IsCompletionAvailableProperty, featureIsAvailable);
+ return featureIsAvailable;
+ }
}
}
diff --git a/src/Language/Impl/Language/Completion/AsyncCompletionSession.cs b/src/Language/Impl/Language/Completion/AsyncCompletionSession.cs
index 0bfbdb6..27cf094 100644
--- a/src/Language/Impl/Language/Completion/AsyncCompletionSession.cs
+++ b/src/Language/Impl/Language/Completion/AsyncCompletionSession.cs
@@ -8,8 +8,10 @@ using System.Threading.Tasks;
using Microsoft.VisualStudio.Language.Intellisense;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Operations;
using Microsoft.VisualStudio.Text.Utilities;
using Microsoft.VisualStudio.Threading;
+using Microsoft.VisualStudio.Utilities;
namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
{
@@ -21,16 +23,18 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
{
// Available data and services
// TODO: consider storing MappingPoint instead of SnapshotPoint
- private readonly IDictionary<IAsyncCompletionItemSource, SnapshotPoint> _completionProviders;
+ private readonly IDictionary<IAsyncCompletionItemSource, SnapshotPoint> _completionSources;
private readonly IAsyncCompletionService _completionService;
private readonly JoinableTaskFactory _jtf;
- private readonly ICompletionUIProvider _uiFactory;
- private readonly IAsyncCompletionBroker _broker;
+ private readonly ICompletionPresenterProvider _uiFactory;
+ private readonly AsyncCompletionBroker _broker;
private readonly ITextView _view;
private readonly TelemetryData _telemetryData;
+ private readonly ITextStructureNavigator _textStructureNavigator;
+ private readonly IGuardedOperations _guardedOperations;
// Presentation:
- ICompletionUI _gui; // Must be accessed from GUI thread
+ ICompletionPresenter _gui; // Must be accessed from GUI thread
const int FirstIndex = 0;
readonly int PageStepSize;
@@ -44,122 +48,236 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
// When set, we won't send dismissed telemetry
private bool _committed;
- public AsyncCompletionSession(JoinableTaskFactory jtf, ICompletionUIProvider uiFactory, IDictionary<IAsyncCompletionItemSource, SnapshotPoint> providers, IAsyncCompletionService service, AsyncCompletionBroker broker, ITextView view)
+ public event EventHandler<CompletionItemEventArgs> ItemCommitted;
+ public event EventHandler Dismissed;
+ public event EventHandler<CompletionItemsWithHighlightEventArgs> ItemsUpdated;
+
+ public ITextView TextView => _view;
+
+ public AsyncCompletionSession(JoinableTaskFactory jtf, ICompletionPresenterProvider uiFactory,
+ IDictionary<IAsyncCompletionItemSource, SnapshotPoint> completionSources,
+ IAsyncCompletionService completionService, AsyncCompletionBroker broker, ITextView view)
{
_jtf = jtf;
_uiFactory = uiFactory;
_broker = broker;
- _completionProviders = providers;
- _completionService = service;
+ _completionSources = completionSources;
+ _completionService = completionService;
_view = view;
+ _textStructureNavigator = broker.TextStructureNavigatorSelectorService?.GetTextStructureNavigator(view.TextBuffer);
+ _guardedOperations = broker.GuardedOperations;
_telemetryData = new TelemetryData(broker);
PageStepSize = uiFactory?.ResultsPerPage ?? 1;
_view.Caret.PositionChanged += OnCaretPositionChanged;
}
- public void Commit(string edit)
+ bool IAsyncCompletionSession.CommitIfUnique(CancellationToken token)
+ {
+ // Note that this will deadlock if OpenOrUpdate wasn't called ahead of time.
+ var lastModel = _computation.WaitAndGetResult();
+ if (lastModel.UniqueItem != null)
+ {
+ Commit(default(char), lastModel.UniqueItem, token);
+ return true;
+ }
+ else if (lastModel.PresentedItems.Length == 1)
+ {
+ Commit(default(char), lastModel.PresentedItems[0].CompletionItem, token);
+ return true;
+ }
+ else
+ {
+ // Show the UI, because waitAndGetResult canceled showing the UI.
+ UpdateUiInner(lastModel); // We are on the UI thread, so we may call UpdateUiInner
+ return false;
+ }
+ }
+
+ void IAsyncCompletionSession.Commit(CancellationToken token, char typedChar)
{
var lastModel = _computation.WaitAndGetResult();
- if (lastModel.UseSuggestionMode && !String.IsNullOrEmpty(edit))
+ if (lastModel.SelectSuggestionMode && !typedChar.Equals(default(char)))
return; // In suggestion mode, allow only explicit commits (click, tab, e.g. not tied to a text change)
- else if (lastModel.SelectSuggestionMode && lastModel.SuggestionIsEmpty)
+ else if (lastModel.SelectSuggestionMode && string.IsNullOrWhiteSpace(lastModel.SuggestionModeItem?.InsertText))
return; // In suggestion mode, don't commit empty suggestion (suggestion item temporarily shows description of suggestion mode)
- else if (lastModel.SelectSuggestionMode && !lastModel.SuggestionIsEmpty)
- Commit(edit, lastModel.SuggestionModeItem.CompletionItem);
+ else if (lastModel.SelectSuggestionMode)
+ Commit(typedChar, lastModel.SuggestionModeItem, token);
else if (!lastModel.PresentedItems.Any())
return; // There is nothing to commit
else
- Commit(edit, lastModel.PresentedItems.ElementAt(lastModel.SelectedIndex).CompletionItem);
+ Commit(typedChar, lastModel.PresentedItems[lastModel.SelectedIndex].CompletionItem, token);
}
- public void Commit(string edit, CompletionItem itemToCommit)
+ private void Commit(char typedChar, CompletionItem itemToCommit, CancellationToken token)
{
var lastModel = _computation.WaitAndGetResult();
- // TODO: ensure we are on the UI thread
+ if (!_jtf.Context.IsOnMainThread)
+ throw new InvalidOperationException($"{nameof(IAsyncCompletionSession)}.{nameof(IAsyncCompletionSession.Commit)} must be callled from UI thread.");
_telemetryData.UiStopwatch.Restart();
- if (itemToCommit.CustomCommit)
+ if (itemToCommit.UseCustomCommit)
{
// Pass appropriate buffer to the item's provider
- var buffer = _completionProviders[itemToCommit.Source].Snapshot.TextBuffer;
- itemToCommit.Source.CustomCommit(_view, buffer, itemToCommit, lastModel.ApplicableSpan, edit);
+ var buffer = _completionSources[itemToCommit.Source].Snapshot.TextBuffer;
+ _guardedOperations.CallExtensionPoint(
+ errorSource: itemToCommit.Source,
+ call: () => itemToCommit.Source.CustomCommit(_view, buffer, itemToCommit, lastModel.ApplicableSpan, typedChar, token));
}
else
{
- var buffer = _view.TextBuffer;
- var bufferEdit = buffer.CreateEdit();
- bufferEdit.Replace(lastModel.ApplicableSpan.GetSpan(buffer.CurrentSnapshot), itemToCommit.InsertText + edit);
- bufferEdit.Apply();
+ InsertIntoBuffer(_view, lastModel, itemToCommit.InsertText, typedChar);
}
_telemetryData.UiStopwatch.Stop();
- _telemetryData.RecordCommitAndSend(_telemetryData.UiStopwatch.ElapsedMilliseconds, itemToCommit, edit);
+ _telemetryData.RecordCommitAndSend(_telemetryData.UiStopwatch.ElapsedMilliseconds, itemToCommit, typedChar);
_committed = true;
+ ItemCommitted?.Invoke(this, new CompletionItemEventArgs(itemToCommit));
+ ((IAsyncCompletionSession)this).Dismiss();
}
- void IAsyncCompletionSession.DismissAndHide()
+ private void InsertIntoBuffer(ITextView view, CompletionModel model, string insertText, char typeChar)
{
- // TODO: protect from race conditions when we get two Dismiss requests
+ var buffer = view.TextBuffer;
+ var bufferEdit = buffer.CreateEdit();
+ var textToInsert = typeChar.Equals(default(char)) ? insertText : insertText + typeChar;
+ bufferEdit.Replace(model.ApplicableSpan.GetSpan(buffer.CurrentSnapshot), textToInsert);
+ bufferEdit.Apply();
+ }
+
+ void IAsyncCompletionSession.Dismiss()
+ {
+ if (_isDismissed)
+ return;
+
_isDismissed = true;
+ _broker.DismissSession(this);
+ Dismissed?.Invoke(this, EventArgs.Empty);
_view.Caret.PositionChanged -= OnCaretPositionChanged;
_computationCancellation.Cancel();
+ _computation = null;
if (!_committed)
_telemetryData.RecordDismissedAndSend();
- if (_gui == null)
- return; // nothing to hide
-
- // TODO: ensure we are on the UI thread
- _gui.FiltersChanged -= OnFiltersChanged;
- _gui.CompletionItemCommitted -= OnItemCommitted;
- _gui.Hide();
- _gui.Dispose();
+ if (_gui != null)
+ {
+ _guardedOperations.CallExtensionPoint(
+ errorSource: _gui,
+ call: async () =>
+ {
+ await _jtf.SwitchToMainThreadAsync();
+ _gui.FiltersChanged -= OnFiltersChanged;
+ _gui.CommitRequested -= OnCommitRequested;
+ _gui.CompletionItemSelected -= OnItemSelected;
+ _gui.CompletionClosed -= OnGuiClosed;
+ _gui.Close();
+ });
+ _gui = null;
+ }
}
- public void OpenOrUpdate(ITextView view, ITrackingSpan trackedEdit, CompletionTrigger trigger, SnapshotPoint triggerLocation)
+ void IAsyncCompletionSession.OpenOrUpdate(ITextView textView, CompletionTrigger trigger, SnapshotPoint triggerLocation)
{
if (_computation == null)
{
_computation = new ModelComputation<CompletionModel>(PrioritizedTaskScheduler.AboveNormalInstance, _computationCancellation.Token);
- _computation.Enqueue((model, token) => GetInitialModel(view, trackedEdit, trigger, triggerLocation, token));
+ _computation.Enqueue((model, token) => GetInitialModel(textView, trigger, triggerLocation, token));
}
var taskId = Interlocked.Increment(ref _lastFilteringTaskId);
- _computation.Enqueue((model, token) => UpdateSnapshot(model, trigger, triggerLocation, token, taskId), UpdateUI);
+ _computation.Enqueue((model, token) => UpdateSnapshot(model, trigger, FromCompletionTriggerReason(trigger.Reason), triggerLocation, token, taskId), UpdateUi);
+ }
+
+ internal void InvokeAndCommitIfUnique(ITextView view, CompletionTrigger trigger, SnapshotPoint triggerLocation, CancellationToken token)
+ {
+ ((IAsyncCompletionSession)this).OpenOrUpdate(view, trigger, triggerLocation);
+ if (((IAsyncCompletionSession)this).CommitIfUnique(token))
+ {
+ ((IAsyncCompletionSession)this).Dismiss();
+ }
}
- public void SelectDown()
+ private static CompletionFilterReason FromCompletionTriggerReason(CompletionTriggerReason reason)
{
- _computation.Enqueue((model, token) => UpdateSelectedItem(model, +1, token), UpdateUI);
+ switch (reason)
+ {
+ case CompletionTriggerReason.Invoke:
+ case CompletionTriggerReason.InvokeAndCommitIfUnique:
+ return CompletionFilterReason.Initial;
+ case CompletionTriggerReason.Insertion:
+ return CompletionFilterReason.Insertion;
+ case CompletionTriggerReason.Deletion:
+ return CompletionFilterReason.Deletion;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(reason));
+ }
}
- public void SelectPageDown()
+ #region Internal methods accessed by the command handlers
+
+ internal void ToggleSuggestionMode()
{
- _computation.Enqueue((model, token) => UpdateSelectedItem(model, +PageStepSize, token), UpdateUI);
+ _computation.Enqueue((model, token) => ToggleCompletionModeInner(model, token), UpdateUi);
}
- public void SelectUp()
+ internal void SelectDown()
{
- _computation.Enqueue((model, token) => UpdateSelectedItem(model, -1, token), UpdateUI);
+ _computation.Enqueue((model, token) => UpdateSelectedItem(model, +1, token), UpdateUi);
}
- public void SelectPageUp()
+ internal void SelectPageDown()
{
- _computation.Enqueue((model, token) => UpdateSelectedItem(model, -PageStepSize, token), UpdateUI);
+ _computation.Enqueue((model, token) => UpdateSelectedItem(model, +PageStepSize, token), UpdateUi);
}
- private void OnFiltersChanged(object sender, CompletionFilterChangedEventArgs e)
+ internal void SelectUp()
+ {
+ _computation.Enqueue((model, token) => UpdateSelectedItem(model, -1, token), UpdateUi);
+ }
+
+ internal void SelectPageUp()
+ {
+ _computation.Enqueue((model, token) => UpdateSelectedItem(model, -PageStepSize, token), UpdateUi);
+ }
+
+ #endregion
+
+ private void OnFiltersChanged(object sender, CompletionFilterChangedEventArgs args)
{
var taskId = Interlocked.Increment(ref _lastFilteringTaskId);
- _computation.Enqueue((model, token) => UpdateFilters(model, e.Filters, token, taskId), UpdateUI);
+ _computation.Enqueue((model, token) => UpdateFilters(model, args.Filters, token, taskId), UpdateUi);
}
- private void OnItemCommitted(object sender, CompletionItemCommittedEventArgs e)
+ private void OnCommitRequested(object sender, CompletionItemEventArgs args)
{
- Commit(String.Empty, e.Item);
- _broker.Dismiss(_view);
+ Commit(default(char), args.Item, default(CancellationToken));
+ }
+
+ private void OnItemSelected(object sender, CompletionItemSelectedEventArgs args)
+ {
+ _computation.Enqueue((model, token) => UpdateSelectedItem(model, args.SelectedItem, args.SuggestionModeSelected, token)); // Note: we do not dispatch the call to update UI afterwards.
+ }
+
+ private void OnGuiClosed(object sender, CompletionClosedEventArgs args)
+ {
+ ((IAsyncCompletionSession)this).Dismiss();
+ }
+
+ bool IAsyncCompletionSession.ShouldCommit(ITextView view, char typeChar, SnapshotPoint triggerLocation)
+ {
+ foreach (var contentType in CompletionUtilities.GetBuffersForTriggerPoint(view, triggerLocation).Select(b => b.ContentType))
+ {
+ if (_broker.TryGetKnownCommitCharacters(contentType, out var commitChars))
+ {
+ if (commitChars.Contains(typeChar))
+ {
+ if (ShouldCommit(view, typeChar, triggerLocation))
+ return true;
+ }
+ }
+ }
+ return false;
}
/// <summary>
@@ -171,60 +289,83 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
{
// http://source.roslyn.io/#Microsoft.CodeAnalysis.EditorFeatures/Implementation/IntelliSense/Completion/Controller_CaretPositionChanged.cs,40
// enqueue a task to see if the caret is still within the broundary
- _computation.Enqueue((model, token) => HandleCaretPositionChanged(model, e.NewPosition), UpdateUI);
+ _computation.Enqueue((model, token) => HandleCaretPositionChanged(model, e.NewPosition), UpdateUi);
}
- private async Task UpdateUI(CompletionModel model)
+ private async Task UpdateUi(CompletionModel model)
{
if (_uiFactory == null) return;
await _jtf.SwitchToMainThreadAsync();
+ UpdateUiInner(model);
+ await TaskScheduler.Default;
+ }
+ private void UpdateUiInner(CompletionModel model)
+ {
if (_isDismissed)
return;
_telemetryData.UiStopwatch.Restart();
if (_gui == null)
{
- _gui = _uiFactory.GetUI(_view);
- _gui.Open(new CompletionPresentation(model.PresentedItems, model.Filters, model.ApplicableSpan, model.UseSoftSelection, model.UseSuggestionMode, model.SelectSuggestionMode, model.SelectedIndex, model.SuggestionModeItem));
- _gui.FiltersChanged += OnFiltersChanged;
- _gui.CompletionItemCommitted += OnItemCommitted;
+ _gui = _guardedOperations.CallExtensionPoint(errorSource: _uiFactory, call: () => _uiFactory.GetOrCreate(_view), valueOnThrow: null);
+ if (_gui != null)
+ {
+ _guardedOperations.CallExtensionPoint(
+ errorSource: _gui,
+ call: () =>
+ {
+ _gui = _uiFactory.GetOrCreate(_view);
+ _gui.Open(new CompletionPresentationViewModel(model.PresentedItems, model.Filters, model.ApplicableSpan, model.UseSoftSelection,
+ model.DisplaySuggestionMode, model.SelectSuggestionMode, model.SelectedIndex, model.SuggestionModeItem, model.SuggestionModeDescription));
+ _gui.FiltersChanged += OnFiltersChanged;
+ _gui.CommitRequested += OnCommitRequested;
+ _gui.CompletionItemSelected += OnItemSelected;
+ _gui.CompletionClosed += OnGuiClosed;
+ });
+ }
}
else
{
- _gui.Update(new CompletionPresentation(model.PresentedItems, model.Filters, model.ApplicableSpan, model.UseSoftSelection, model.UseSuggestionMode, model.SelectSuggestionMode, model.SelectedIndex, model.SuggestionModeItem));
+ _guardedOperations.CallExtensionPoint(
+ errorSource: _gui,
+ call: () => _gui.Update(new CompletionPresentationViewModel(model.PresentedItems, model.Filters, model.ApplicableSpan, model.UseSoftSelection,
+ model.DisplaySuggestionMode, model.SelectSuggestionMode, model.SelectedIndex, model.SuggestionModeItem, model.SuggestionModeDescription)));
}
_telemetryData.UiStopwatch.Stop();
_telemetryData.RecordRendering(_telemetryData.UiStopwatch.ElapsedMilliseconds);
-
- await TaskScheduler.Default;
}
/// <summary>
/// Creates a new model and populates it with initial data
/// </summary>
- private async Task<CompletionModel> GetInitialModel(ITextView view, ITrackingSpan trackedEdit, CompletionTrigger trigger, SnapshotPoint triggerLocation, CancellationToken token)
+ private async Task<CompletionModel> GetInitialModel(ITextView textView, CompletionTrigger trigger, SnapshotPoint triggerLocation, CancellationToken token)
{
_telemetryData.ComputationStopwatch.Restart();
// Map the trigger location to respective view for each completion provider
- var nestedResults = await Task.WhenAll(_completionProviders.Select(p => p.Key.GetCompletionContextAsync(trigger, p.Value)));
- var originalCompletionItems = nestedResults.SelectMany(n => n.Items).ToImmutableArray();
-
- var availableFilters = nestedResults
- .SelectMany(n => n.Items)
+ var nestedResults = await Task.WhenAll(
+ _completionSources.Select(
+ p => _guardedOperations.CallExtensionPointAsync<CompletionContext>(
+ errorSource: p.Key,
+ asyncCall: () => p.Key.GetCompletionContextAsync(trigger, p.Value, token),
+ valueOnThrow: null
+ )));
+ var initialCompletionItems = nestedResults.Where(n => n != null && !n.Items.IsDefaultOrEmpty).SelectMany(n => n.Items).ToImmutableArray();
+
+ var availableFilters = initialCompletionItems
.SelectMany(n => n.Filters)
.Distinct()
.Select(n => new CompletionFilterWithState(n, true))
.ToImmutableArray();
- // Note: do not use the tracked edit from the editor. Exclusively rely on data from the completion providers
var spans = nestedResults
.Select(n => n.ApplicableToSpan)
- .Select(s => view.BufferGraph.CreateMappingSpan(s, SpanTrackingMode.EdgeNegative))
+ .Select(s => textView.BufferGraph.CreateMappingSpan(s, SpanTrackingMode.EdgeNegative))
.Select(s => new SnapshotSpan(
s.Start.GetPoint(triggerLocation.Snapshot, PositionAffinity.Predecessor).Value,
s.End.GetPoint(triggerLocation.Snapshot, PositionAffinity.Predecessor).Value));
+ //var extentFromStructureNavigator = _textStructureNavigator?.GetExtentOfWord(triggerLocation - 1).Span;
var extent = new SnapshotSpan(
spans.Min(n => n.Start),
spans.Max(n => n.End));
@@ -232,32 +373,49 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
var useSoftSelection = nestedResults.Any(n => n.UseSoftSelection);
var useSuggestionMode = nestedResults.Any(n => n.UseSuggestionMode);
- var suggestionModeDescription = nestedResults.FirstOrDefault(n => !String.IsNullOrEmpty(n.SuggestionModeDescription)).SuggestionModeDescription ?? String.Empty;
+ var suggestionModeDescription = nestedResults.FirstOrDefault(n => !string.IsNullOrEmpty(n.SuggestionModeDescription))?.SuggestionModeDescription ?? string.Empty;
+
+ _telemetryData.ComputationStopwatch.Stop();
+ _telemetryData.RecordInitialModel(_telemetryData.ComputationStopwatch.ElapsedMilliseconds, _completionSources.Keys, initialCompletionItems.Length, _completionService);
+ _telemetryData.ComputationStopwatch.Restart();
+ var sortedList = await _guardedOperations.CallExtensionPointAsync(
+ errorSource: _completionService,
+ asyncCall: () => _completionService.SortCompletionListAsync(initialCompletionItems, trigger.Reason, triggerLocation.Snapshot, applicableSpan, _view, token),
+ valueOnThrow: initialCompletionItems);
_telemetryData.ComputationStopwatch.Stop();
- _telemetryData.RecordInitialModel(_telemetryData.ComputationStopwatch.ElapsedMilliseconds, _completionProviders.Keys, originalCompletionItems.Length, _completionService);
+ _telemetryData.RecordProcessing(_telemetryData.ComputationStopwatch.ElapsedMilliseconds, initialCompletionItems.Length);
- return new CompletionModel(originalCompletionItems, applicableSpan, triggerLocation.Snapshot, availableFilters, useSoftSelection, useSuggestionMode, suggestionModeDescription);
+ return new CompletionModel(initialCompletionItems, sortedList, applicableSpan, trigger.Reason, triggerLocation.Snapshot,
+ availableFilters, useSoftSelection, useSuggestionMode, suggestionModeDescription, suggestionModeItem: null);
}
/// <summary>
/// User has moved the caret. Ensure that the caret is still within the applicable span. If not, dismiss the session.
/// </summary>
/// <returns></returns>
- private async Task<CompletionModel> HandleCaretPositionChanged(CompletionModel model, CaretPosition caretPosition)
+ private Task<CompletionModel> HandleCaretPositionChanged(CompletionModel model, CaretPosition caretPosition)
{
if (!(model.ApplicableSpan.GetSpan(caretPosition.VirtualBufferPosition.Position.Snapshot).IntersectsWith(new SnapshotSpan(caretPosition.VirtualBufferPosition.Position, 0))))
{
- await _jtf.SwitchToMainThreadAsync();
- _broker.Dismiss(_view);
+ ((IAsyncCompletionSession)this).Dismiss();
}
- return model;
+ return Task.FromResult(model);
+ }
+
+ /// <summary>
+ /// User has moved the caret. Ensure that the caret is still within the applicable span. If not, dismiss the session.
+ /// </summary>
+ /// <returns></returns>
+ private Task<CompletionModel> ToggleCompletionModeInner(CompletionModel model, CancellationToken token)
+ {
+ return Task.FromResult(model.WithSuggestionModeActive(!model.DisplaySuggestionMode));
}
/// <summary>
/// User has typed
/// </summary>
- private async Task<CompletionModel> UpdateSnapshot(CompletionModel model, CompletionTrigger trigger, SnapshotPoint triggerLocation, CancellationToken token, int thisId)
+ private async Task<CompletionModel> UpdateSnapshot(CompletionModel model, CompletionTrigger trigger, CompletionFilterReason filterReason, SnapshotPoint triggerLocation, CancellationToken token, int thisId)
{
// Filtering got preempted, so store the most recent snapshot for the next time we filter
if (token.IsCancellationRequested || thisId != _lastFilteringTaskId)
@@ -267,68 +425,76 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
// 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))
{
- await _jtf.SwitchToMainThreadAsync();
- _broker.Dismiss(_view); // We need to dismiss through the broker, so that it updates its state.
+ ((IAsyncCompletionSession)this).Dismiss();
return model;
}
_telemetryData.ComputationStopwatch.Restart();
var filteredCompletion = await _completionService.UpdateCompletionListAsync(
- model.AllItems,
- trigger,
+ model.InitialItems,
+ model.InitialTriggerReason,
+ filterReason,
triggerLocation.Snapshot,
model.ApplicableSpan,
- model.Filters);
+ model.Filters,
+ _view,
+ token);
- if (model.UseSuggestionMode)
- {
- var filterText = model.ApplicableSpan.GetText(triggerLocation.Snapshot);
- CompletionItemWithHighlight suggestionModeItem;
- if (String.IsNullOrWhiteSpace(filterText))
- {
- suggestionModeItem = new CompletionItemWithHighlight(new CompletionItem(model.SuggestionModeDescription, null), ImmutableArray<Span>.Empty);
- model = model.WithEmptySuggestionItem(suggestionModeItem);
- }
- else
- {
- suggestionModeItem = new CompletionItemWithHighlight(new CompletionItem(filterText, null), ImmutableArray.Create<Span>(new Span(0, filterText.Length)));
- model = model.WithSuggestionItem(suggestionModeItem);
- }
- }
+ // Prevent null references when service returns default(ImmutableArray)
+ ImmutableArray<CompletionItemWithHighlight> returnedItems = filteredCompletion.Items.IsDefault
+ ? ImmutableArray<CompletionItemWithHighlight>.Empty
+ : filteredCompletion.Items;
_telemetryData.ComputationStopwatch.Stop();
- _telemetryData.RecordProcessing(_telemetryData.ComputationStopwatch.ElapsedMilliseconds, filteredCompletion.Items.Count());
+ _telemetryData.RecordProcessing(_telemetryData.ComputationStopwatch.ElapsedMilliseconds, returnedItems.Length);
- // TODO: if filtered completion has no items to display,
- // reuse previous filtered completion, but with soft selection
- return model.WithSnapshot(triggerLocation.Snapshot).WithPresentedItems(filteredCompletion.Items, filteredCompletion.SelectedItemIndex);
+ if (filteredCompletion.SelectionMode == CompletionItemSelection.SoftSelected)
+ model = model.WithSoftSelection(true);
+ else if (filteredCompletion.SelectionMode == CompletionItemSelection.Selected)
+ model = model.WithSoftSelection(false);
+
+ // Prepare the suggestionModeItem if we ever change the mode
+ var enteredText = model.ApplicableSpan.GetText(triggerLocation.Snapshot);
+ if (string.IsNullOrWhiteSpace(enteredText))
+ enteredText = "_"; // TODO: change CompletionItem to allow for empty display text and remove this code.
+ var suggestionModeItem = new CompletionItem(enteredText, SuggestionModeCompletionItemSource.Instance);
+
+ // TODO: guarded operations
+ ItemsUpdated?.Invoke(this, new CompletionItemsWithHighlightEventArgs(returnedItems));
+
+ // TODO: combine this chain into a single method:
+ return model.WithSnapshot(triggerLocation.Snapshot).WithUniqueItem(filteredCompletion.UniqueItem)
+ .WithSuggestionModeItem(suggestionModeItem).WithPresentedItems(returnedItems, filteredCompletion.SelectedItemIndex);
}
/// <summary>
- /// User has changed a filter
+ /// Reacts to user toggling a filter
/// </summary>
- private async Task<CompletionModel> UpdateFilters(CompletionModel model, ImmutableArray<CompletionFilterWithState> filters, CancellationToken token, int thisId)
+ private async Task<CompletionModel> UpdateFilters(CompletionModel model, ImmutableArray<CompletionFilterWithState> newFilters, CancellationToken token, int thisId)
{
_telemetryData.RecordChangingFilters();
// Filtering got preempted, so store the most updated filters for the next time we filter
if (token.IsCancellationRequested || thisId != _lastFilteringTaskId)
- return model.WithFilters(filters);
+ return model.WithFilters(newFilters);
var filteredCompletion = await _completionService.UpdateCompletionListAsync(
- model.AllItems,
- new CompletionTrigger(CompletionTriggerReason.FilterChange),
+ model.InitialItems,
+ model.InitialTriggerReason,
+ CompletionFilterReason.FilterChange,
model.Snapshot,
model.ApplicableSpan,
- filters);
+ newFilters,
+ _view,
+ token);
- return model.WithFilters(filters).WithPresentedItems(filteredCompletion.Items, filteredCompletion.SelectedItemIndex);
+ return model.WithFilters(newFilters).WithPresentedItems(filteredCompletion.Items, filteredCompletion.SelectedItemIndex);
}
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
/// <summary>
- /// User is scrolling the list
+ /// Reacts to user scrolling the list
/// </summary>
private async Task<CompletionModel> UpdateSelectedItem(CompletionModel model, int offset, CancellationToken token)
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
@@ -338,7 +504,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
if (!model.PresentedItems.Any())
{
// No-op if there are no items
- if (model.UseSuggestionMode)
+ if (model.DisplaySuggestionMode)
{
// Unless there is a suggestion mode item. Select it.
return model.WithSuggestionItemSelected();
@@ -353,7 +519,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
{
if (currentIndex == lastIndex)
{
- if (model.UseSuggestionMode)
+ if (model.DisplaySuggestionMode)
return model.WithSuggestionItemSelected();
else
return model.WithSelectedIndex(FirstIndex);
@@ -371,7 +537,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
else if (currentIndex == FirstIndex)
{
// The first item is selected. If there is a suggestion, select it.
- if (model.UseSuggestionMode)
+ if (model.DisplaySuggestionMode)
return model.WithSuggestionItemSelected();
else
return model.WithSelectedIndex(lastIndex);
@@ -381,6 +547,31 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
}
}
+#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
+ /// <summary>
+ /// Reacts to user selecting a specific item in the list
+ /// </summary>
+ private async Task<CompletionModel> UpdateSelectedItem(CompletionModel model, CompletionItem selectedItem, bool suggestionModeSelected, CancellationToken token)
+#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
+ {
+ if (suggestionModeSelected)
+ {
+ return model.WithSuggestionItemSelected();
+ }
+ else
+ {
+ for (int i = 0; i < model.PresentedItems.Length; i++)
+ {
+ if (model.PresentedItems[i].CompletionItem == selectedItem)
+ {
+ return model.WithSelectedIndex(i);
+ }
+ }
+ // This item is not in the model
+ return model;
+ }
+ }
+
/// <summary>
/// This method creates a mapping point from the top-buffer trigger location
/// then iterates through completion item sources pertinent to this session.
@@ -388,19 +579,26 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
/// Ensures that we work only with sources applicable to current location
/// and returns whether any source would like to commit completion
/// </summary>
- public bool ShouldCommit(ITextView view, string edit, SnapshotPoint triggerLocation)
+ public bool ShouldCommit(ITextView view, char typeChar, SnapshotPoint triggerLocation)
{
var mappingPoint = view.BufferGraph.CreateMappingPoint(triggerLocation, PointTrackingMode.Negative);
- return _completionProviders
+ return _completionSources
.Select(p => (p, mappingPoint.GetPoint(p.Value.Snapshot.TextBuffer, PositionAffinity.Predecessor)))
.Where(n => n.Item2.HasValue)
- .Any(n => n.Item1.Key.ShouldCommitCompletion(edit, n.Item2.Value));
- // Remove previous line and uncomment the following lines to get the specific IAsyncCompletionItemSource that wanted to commit
- /*
- .Where(n => n.Item1.Key.ShouldCommitCompletion(edit, n.Item2.Value))
- .Select(n => n.Item1.Key)
- .FirstOrDefault();
- */
+ .Any(n => _guardedOperations.CallExtensionPoint(
+ errorSource: n.Item1.Key,
+ call: () => n.Item1.Key.ShouldCommitCompletion(typeChar, n.Item2.Value),
+ valueOnThrow: false));
+ }
+
+ public ImmutableArray<CompletionItem> GetVisibleItems(CancellationToken token)
+ {
+ throw new NotImplementedException();
+ }
+
+ public CompletionItem GetSelectedItem(CancellationToken token)
+ {
+ throw new NotImplementedException();
}
private class TelemetryData
@@ -473,7 +671,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
internal void RecordInitialModel(long processingTime, ICollection<IAsyncCompletionItemSource> sources, int initialItemCount, IAsyncCompletionService service)
{
InitialItemCount = initialItemCount;
- InitialItemSources = String.Join(" ", sources.Select(n => _broker.GetItemSourceName(n)));
+ InitialItemSources = string.Join(" ", sources.Select(n => _broker.GetItemSourceName(n)));
InitialModelDuration = processingTime;
CompletionService = _broker.GetCompletionServiceName(service); // Service does not participate in getting the initial model, but this is a good place to get this data
}
@@ -505,7 +703,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
UserEverSetFilters = true;
}
- internal void RecordCommitAndSend(long commitDuration, CompletionItem committedItem, string edit)
+ internal void RecordCommitAndSend(long commitDuration, CompletionItem committedItem, char typeChar)
{
_logger?.PostEvent(TelemetryEventType.Operation,
TelemetryData.CommitEvent,
@@ -513,7 +711,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
(InitialItemSourcesProperty, InitialItemSources ?? string.Empty),
(InitialItemCountProperty, InitialItemCount),
(InitialModelDurationProperty, InitialModelDuration),
- (CompletionServiceProperty, CompletionService),
+ (CompletionServiceProperty, CompletionService ?? string.Empty),
(TotalProcessingDurationProperty, TotalProcessingDuration),
(TotalProcessingCountProperty, TotalProcessingCount),
(InitialRenderingDurationProperty, InitialRenderingDuration),
@@ -523,10 +721,10 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
(UserEverSetFiltersProperty, UserEverSetFilters),
(UserLastScrolledProperty, UserLastScrolled),
(LastItemCountProperty, LastItemCount),
- (CustomCommitProperty, committedItem.CustomCommit),
- (CommittedItemSourceProperty, GetItemSourceName(committedItem.Source)),
+ (CustomCommitProperty, committedItem.UseCustomCommit),
+ (CommittedItemSourceProperty, GetItemSourceName(committedItem.Source) ?? string.Empty),
(CommitDurationProperty, commitDuration),
- (CommitTriggerProperty, edit ?? string.Empty)
+ (CommitTriggerProperty, typeChar)
);
}
@@ -538,7 +736,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
(InitialItemSourcesProperty, InitialItemSources ?? string.Empty),
(InitialItemCountProperty, InitialItemCount),
(InitialModelDurationProperty, InitialModelDuration),
- (CompletionServiceProperty, CompletionService),
+ (CompletionServiceProperty, CompletionService ?? string.Empty),
(LastItemCountProperty, LastItemCount),
(TotalProcessingDurationProperty, TotalProcessingDuration),
(TotalProcessingCountProperty, TotalProcessingCount),
diff --git a/src/Language/Impl/Language/Completion/CompletionCommandHandlers.cs b/src/Language/Impl/Language/Completion/CompletionCommandHandlers.cs
new file mode 100644
index 0000000..a50b6fb
--- /dev/null
+++ b/src/Language/Impl/Language/Completion/CompletionCommandHandlers.cs
@@ -0,0 +1,329 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.VisualStudio.Commanding;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Editor.Commanding.Commands;
+using Microsoft.VisualStudio.Text.Utilities;
+using Microsoft.VisualStudio.Utilities;
+
+namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
+{
+ /// <summary>
+ /// Reacts to the down arrow command and attempts to scroll the completion list.
+ /// </summary>
+ [Name(nameof(CompletionCommandHandlers))]
+ [ContentType("any")]
+ [Export(typeof(ICommandHandler))]
+ internal sealed class CompletionCommandHandlers :
+ ICommandHandler<DownKeyCommandArgs>,
+ ICommandHandler<PageDownKeyCommandArgs>,
+ ICommandHandler<PageUpKeyCommandArgs>,
+ ICommandHandler<UpKeyCommandArgs>,
+ IChainedCommandHandler<BackspaceKeyCommandArgs>,
+ ICommandHandler<EscapeKeyCommandArgs>,
+ ICommandHandler<InvokeCompletionListCommandArgs>,
+ ICommandHandler<CommitUniqueCompletionListItemCommandArgs>,
+ ICommandHandler<InsertSnippetCommandArgs>,
+ ICommandHandler<ToggleCompletionModeCommandArgs>,
+ IChainedCommandHandler<DeleteKeyCommandArgs>,
+ ICommandHandler<WordDeleteToEndCommandArgs>,
+ ICommandHandler<WordDeleteToStartCommandArgs>,
+ ICommandHandler<ReturnKeyCommandArgs>,
+ ICommandHandler<TabKeyCommandArgs>,
+ IChainedCommandHandler<TypeCharCommandArgs>
+ {
+ [Import]
+ IAsyncCompletionBroker Broker { get; set; }
+
+ [Import]
+ IExperimentationServiceInternal ExperimentationService { get; set; }
+
+ string INamed.DisplayName => Strings.CompletionCommandHandlerName;
+
+ /// <summary>
+ /// Helper method that returns command state for commands
+ /// that are always available - unless the completion feature is available.
+ /// </summary>
+ /// <param name="view"></param>
+ /// <returns></returns>
+ private CommandState Available(ITextView view)
+ {
+ return ModernCompletionFeature.GetFeatureState(ExperimentationService)
+ && Broker.IsCompletionSupported(view)
+ ? CommandState.Available
+ : CommandState.Unspecified;
+ }
+
+ /// <summary>
+ /// Helper method that returns command state for commands
+ /// that are available when completion is active.
+ /// </summary>
+ /// <remarks>
+ /// Completion might be active only if the feature is available, so we're skipping other checks.
+ /// </remarks>
+ private CommandState AvailableIfCompletionIsUp(ITextView view)
+ {
+ return Broker.IsCompletionActive(view)
+ ? CommandState.Available
+ : CommandState.Unspecified;
+ }
+
+ CommandState IChainedCommandHandler<BackspaceKeyCommandArgs>.GetCommandState(BackspaceKeyCommandArgs args, Func<CommandState> nextCommandHandler)
+ => AvailableIfCompletionIsUp(args.TextView);
+
+ void IChainedCommandHandler<BackspaceKeyCommandArgs>.ExecuteCommand(BackspaceKeyCommandArgs args, Action nextCommandHandler, CommandExecutionContext executionContext)
+ {
+ // 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)
+ return;
+
+ var session = Broker.GetSession(args.TextView);
+ if (session != null)
+ {
+ var trigger = new CompletionTrigger(CompletionTriggerReason.Deletion);
+ var location = args.TextView.Caret.Position.BufferPosition;
+ session.OpenOrUpdate(args.TextView, trigger, location);
+ }
+ }
+
+ CommandState ICommandHandler<EscapeKeyCommandArgs>.GetCommandState(EscapeKeyCommandArgs args)
+ => AvailableIfCompletionIsUp(args.TextView);
+
+ bool ICommandHandler<EscapeKeyCommandArgs>.ExecuteCommand(EscapeKeyCommandArgs args, CommandExecutionContext executionContext)
+ {
+ var session = Broker.GetSession(args.TextView);
+ if (session != null)
+ {
+ session.Dismiss();
+ return true;
+ }
+ return false;
+ }
+
+ CommandState ICommandHandler<InvokeCompletionListCommandArgs>.GetCommandState(InvokeCompletionListCommandArgs args)
+ => Available(args.TextView);
+
+ bool ICommandHandler<InvokeCompletionListCommandArgs>.ExecuteCommand(InvokeCompletionListCommandArgs args, CommandExecutionContext executionContext)
+ {
+ var trigger = new CompletionTrigger(CompletionTriggerReason.Invoke);
+ var location = args.TextView.Caret.Position.BufferPosition;
+ var session = Broker.TriggerCompletion(args.TextView, location);
+ session.OpenOrUpdate(args.TextView, trigger, location);
+ return true;
+ }
+
+ CommandState ICommandHandler<CommitUniqueCompletionListItemCommandArgs>.GetCommandState(CommitUniqueCompletionListItemCommandArgs args)
+ => Available(args.TextView);
+
+ bool ICommandHandler<CommitUniqueCompletionListItemCommandArgs>.ExecuteCommand(CommitUniqueCompletionListItemCommandArgs args, CommandExecutionContext executionContext)
+ {
+ var trigger = new CompletionTrigger(CompletionTriggerReason.InvokeAndCommitIfUnique);
+ var location = args.TextView.Caret.Position.BufferPosition;
+ var session = Broker.TriggerCompletion(args.TextView, location);
+ session.OpenOrUpdate(args.TextView, trigger, location);
+ // TODO: figure out dismissing. who should dismiss? here, OpenOrUpdate dismisses. Else, commit dismisses.
+ return true;
+ }
+
+ CommandState ICommandHandler<InsertSnippetCommandArgs>.GetCommandState(InsertSnippetCommandArgs args)
+ => Available(args.TextView);
+
+ bool ICommandHandler<InsertSnippetCommandArgs>.ExecuteCommand(InsertSnippetCommandArgs args, CommandExecutionContext executionContext)
+ {
+ System.Diagnostics.Debug.WriteLine("!!!! InsertSnippetCommandArgs");
+ return false;
+ }
+
+ CommandState ICommandHandler<ToggleCompletionModeCommandArgs>.GetCommandState(ToggleCompletionModeCommandArgs args)
+ => Available(args.TextView);
+
+ bool ICommandHandler<ToggleCompletionModeCommandArgs>.ExecuteCommand(ToggleCompletionModeCommandArgs args, CommandExecutionContext executionContext)
+ {
+ var session = Broker.GetSession(args.TextView) as AsyncCompletionSession; // we are accessing an internal method
+ if (session != null)
+ {
+ session.ToggleSuggestionMode();
+ return true; // TODO: See if the toobar button gets updated.
+ }
+ return false;
+ }
+
+ CommandState IChainedCommandHandler<DeleteKeyCommandArgs>.GetCommandState(DeleteKeyCommandArgs args, Func<CommandState> nextCommandHandler)
+ => AvailableIfCompletionIsUp(args.TextView);
+
+ void IChainedCommandHandler<DeleteKeyCommandArgs>.ExecuteCommand(DeleteKeyCommandArgs args, Action nextCommandHandler, CommandExecutionContext executionContext)
+ {
+ // 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)
+ return;
+
+ var session = Broker.GetSession(args.TextView);
+ if (session != null)
+ {
+ var trigger = new CompletionTrigger(CompletionTriggerReason.Deletion);
+ var location = args.TextView.Caret.Position.BufferPosition;
+ session.OpenOrUpdate(args.TextView, trigger, location);
+ }
+ }
+
+ CommandState ICommandHandler<WordDeleteToEndCommandArgs>.GetCommandState(WordDeleteToEndCommandArgs args)
+ => AvailableIfCompletionIsUp(args.TextView);
+
+ bool ICommandHandler<WordDeleteToEndCommandArgs>.ExecuteCommand(WordDeleteToEndCommandArgs args, CommandExecutionContext executionContext)
+ {
+ var session = Broker.GetSession(args.TextView);
+ if (session != null)
+ {
+ session.Dismiss();
+ return false; // return false so that the editor can handle this event
+ }
+ return false;
+ }
+
+ CommandState ICommandHandler<WordDeleteToStartCommandArgs>.GetCommandState(WordDeleteToStartCommandArgs args)
+ => AvailableIfCompletionIsUp(args.TextView);
+
+ bool ICommandHandler<WordDeleteToStartCommandArgs>.ExecuteCommand(WordDeleteToStartCommandArgs args, CommandExecutionContext executionContext)
+ {
+ var session = Broker.GetSession(args.TextView);
+ if (session != null)
+ {
+ session.Dismiss();
+ return false; // return false so that the editor can handle this event
+ }
+ return false;
+ }
+
+ CommandState ICommandHandler<ReturnKeyCommandArgs>.GetCommandState(ReturnKeyCommandArgs args)
+ => AvailableIfCompletionIsUp(args.TextView);
+
+ bool ICommandHandler<ReturnKeyCommandArgs>.ExecuteCommand(ReturnKeyCommandArgs args, CommandExecutionContext executionContext)
+ {
+ var session = Broker.GetSession(args.TextView);
+ if (session != null)
+ {
+ session.Commit(executionContext.OperationContext.UserCancellationToken);
+ session.Dismiss(); // TODO: Currently the implementation needs UI thread
+ return true;
+ }
+ return false;
+ }
+
+ CommandState ICommandHandler<TabKeyCommandArgs>.GetCommandState(TabKeyCommandArgs args)
+ => AvailableIfCompletionIsUp(args.TextView);
+
+ bool ICommandHandler<TabKeyCommandArgs>.ExecuteCommand(TabKeyCommandArgs args, CommandExecutionContext executionContext)
+ {
+ var session = Broker.GetSession(args.TextView);
+ if (session != null)
+ {
+ session.Commit(executionContext.OperationContext.UserCancellationToken);
+ session.Dismiss(); // TODO: Currently the implementation needs UI thread
+ return true;
+ }
+ return false;
+ }
+
+ CommandState IChainedCommandHandler<TypeCharCommandArgs>.GetCommandState(TypeCharCommandArgs args, Func<CommandState> nextCommandHandler)
+ => Available(args.TextView);
+
+ void IChainedCommandHandler<TypeCharCommandArgs>.ExecuteCommand(TypeCharCommandArgs args, Action nextCommandHandler, CommandExecutionContext executionContext)
+ {
+ // 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)
+ return;
+
+ var view = args.TextView;
+ var location = view.Caret.Position.BufferPosition;
+ var sessionToCommit = Broker.GetSession(args.TextView);
+ if (sessionToCommit?.ShouldCommit(view, args.TypedChar, location) == true)
+ {
+ sessionToCommit.Commit(executionContext.OperationContext.UserCancellationToken, args.TypedChar);
+ sessionToCommit.Dismiss(); // TODO: Currently the implementation needs UI thread
+ // Snapshot has changed when committing. 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);
+ if (session != null)
+ {
+ session.OpenOrUpdate(view, trigger, location);
+ }
+ else if (Broker.ShouldTriggerCompletion(view, args.TypedChar, location))
+ {
+ var newSession = Broker.TriggerCompletion(view, location);
+ newSession.OpenOrUpdate(view, trigger, location);
+ }
+ }
+
+ CommandState ICommandHandler<DownKeyCommandArgs>.GetCommandState(DownKeyCommandArgs args)
+ => AvailableIfCompletionIsUp(args.TextView);
+
+ bool ICommandHandler<DownKeyCommandArgs>.ExecuteCommand(DownKeyCommandArgs args, CommandExecutionContext executionContext)
+ {
+ var session = Broker.GetSession(args.TextView) as AsyncCompletionSession; // we are accessing an internal method
+ if (session != null)
+ {
+ session.SelectDown();
+ return true;
+ }
+ return false;
+ }
+
+ CommandState ICommandHandler<PageDownKeyCommandArgs>.GetCommandState(PageDownKeyCommandArgs args)
+ => AvailableIfCompletionIsUp(args.TextView);
+
+ bool ICommandHandler<PageDownKeyCommandArgs>.ExecuteCommand(PageDownKeyCommandArgs args, CommandExecutionContext executionContext)
+ {
+ var session = Broker.GetSession(args.TextView) as AsyncCompletionSession; // we are accessing an internal method
+ if (session != null)
+ {
+ session.SelectPageDown();
+ return true;
+ }
+ return false;
+ }
+
+ CommandState ICommandHandler<PageUpKeyCommandArgs>.GetCommandState(PageUpKeyCommandArgs args)
+ => AvailableIfCompletionIsUp(args.TextView);
+
+ bool ICommandHandler<PageUpKeyCommandArgs>.ExecuteCommand(PageUpKeyCommandArgs args, CommandExecutionContext executionContext)
+ {
+ var session = Broker.GetSession(args.TextView) as AsyncCompletionSession; // we are accessing an internal method
+ if (session != null)
+ {
+ session.SelectPageUp();
+ return true;
+ }
+ return false;
+ }
+
+ CommandState ICommandHandler<UpKeyCommandArgs>.GetCommandState(UpKeyCommandArgs args)
+ => AvailableIfCompletionIsUp(args.TextView);
+
+ bool ICommandHandler<UpKeyCommandArgs>.ExecuteCommand(UpKeyCommandArgs args, CommandExecutionContext executionContext)
+ {
+ var session = Broker.GetSession(args.TextView) as AsyncCompletionSession; // we are accessing an internal method
+ if (session != null)
+ {
+ session.SelectUp();
+ return true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/src/Language/Impl/Language/Completion/CompletionModel.cs b/src/Language/Impl/Language/Completion/CompletionModel.cs
index 7fd95ac..cda2d2e 100644
--- a/src/Language/Impl/Language/Completion/CompletionModel.cs
+++ b/src/Language/Impl/Language/Completion/CompletionModel.cs
@@ -9,12 +9,17 @@ using Microsoft.VisualStudio.Text;
namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
{
- class CompletionModel
+ sealed class CompletionModel
{
/// <summary>
/// All items, as provided by completion item sources.
/// </summary>
- public readonly ImmutableArray<CompletionItem> AllItems;
+ public readonly ImmutableArray<CompletionItem> InitialItems;
+
+ /// <summary>
+ /// Sorted array of all items, as provided by the completion service.
+ /// </summary>
+ public readonly ImmutableArray<CompletionItem> SortedItems;
/// <summary>
/// Span pertinent to this completion model.
@@ -27,6 +32,11 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
public readonly ITextSnapshot Snapshot;
/// <summary>
+ /// Stores the initial reason this session was triggererd.
+ /// </summary>
+ public readonly CompletionTriggerReason InitialTriggerReason;
+
+ /// <summary>
/// Filters involved in this completion model, including their availability and selection state.
/// </summary>
public readonly ImmutableArray<CompletionFilterWithState> Filters;
@@ -34,7 +44,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
/// <summary>
/// Items to be displayed in the UI.
/// </summary>
- public readonly IEnumerable<CompletionItemWithHighlight> PresentedItems;
+ public readonly ImmutableArray<CompletionItemWithHighlight> PresentedItems;
/// <summary>
/// Index of item to select. Use -1 to select nothing, when suggestion mode item should be selected.
@@ -49,7 +59,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
/// <summary>
/// Whether suggestion mode item should be visible.
/// </summary>
- public readonly bool UseSuggestionMode;
+ public readonly bool DisplaySuggestionMode;
/// <summary>
/// Whether suggestion mode item should be selected.
@@ -57,190 +67,262 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
public readonly bool SelectSuggestionMode;
/// <summary>
- /// Text to display in place of suggestion mode when filtered text is empty.
+ /// <see cref="CompletionItem"/> which contains user-entered text.
+ /// Used to display and commit the suggestion mode item
/// </summary>
- public readonly string SuggestionModeDescription;
+ public readonly CompletionItem SuggestionModeItem;
/// <summary>
- /// Item to display as suggestion mode item
+ /// Text to display in place of suggestion mode when filtered text is empty.
/// </summary>
- public readonly CompletionItemWithHighlight SuggestionModeItem;
+ public readonly string SuggestionModeDescription;
/// <summary>
- /// When true, committing the suggestion mode item is a no-op.
+ /// <see cref="CompletionItem"/> which overrides regular unique item selection.
+ /// When this is null, the single item from <see cref="PresentedItems"/> is used as unique item.
/// </summary>
- public readonly bool SuggestionIsEmpty;
+ public readonly CompletionItem UniqueItem;
/// <summary>
/// Constructor for the initial model
/// </summary>
- public CompletionModel(ImmutableArray<CompletionItem> originalItems, ITrackingSpan applicableSpan, ITextSnapshot snapshot, ImmutableArray<CompletionFilterWithState> filters, bool useSoftSelection, bool useSuggestionMode, string suggestionModeDescription)
+ public CompletionModel(ImmutableArray<CompletionItem> initialItems, ImmutableArray<CompletionItem> sortedItems,
+ ITrackingSpan applicableSpan, CompletionTriggerReason initialTriggerReason, ITextSnapshot snapshot,
+ ImmutableArray<CompletionFilterWithState> filters, bool useSoftSelection, bool useSuggestionMode, string suggestionModeDescription, CompletionItem suggestionModeItem)
{
- AllItems = originalItems;
+ InitialItems = initialItems;
+ SortedItems = sortedItems;
ApplicableSpan = applicableSpan;
+ InitialTriggerReason = initialTriggerReason;
Snapshot = snapshot;
Filters = filters;
SelectedIndex = 0;
UseSoftSelection = useSoftSelection;
- UseSuggestionMode = useSuggestionMode;
+ DisplaySuggestionMode = useSuggestionMode;
SelectSuggestionMode = useSuggestionMode;
SuggestionModeDescription = suggestionModeDescription;
- SuggestionModeItem = CompletionItemWithHighlight.Empty;
+ SuggestionModeItem = suggestionModeItem;
+ UniqueItem = null;
}
/// <summary>
/// Private constructor for the With* methods
/// </summary>
- private CompletionModel(ImmutableArray<CompletionItem> originalItems, ITrackingSpan applicableSpan, ITextSnapshot snapshot, ImmutableArray<CompletionFilterWithState> filters, IEnumerable<CompletionItemWithHighlight> presentedItems, bool useSoftSelection, bool useSuggestionMode, string suggestionModeDescription, int selectedIndex, bool selectSuggestionMode, CompletionItemWithHighlight suggestionModeItem, bool suggestionIsEmpty)
+ 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)
{
- AllItems = originalItems;
+ InitialItems = initialItems;
+ SortedItems = sortedItems;
ApplicableSpan = applicableSpan;
+ InitialTriggerReason = initialTriggerReason;
Snapshot = snapshot;
Filters = filters;
PresentedItems = presentedItems;
SelectedIndex = selectedIndex;
UseSoftSelection = useSoftSelection;
- UseSuggestionMode = useSuggestionMode;
+ DisplaySuggestionMode = useSuggestionMode;
SelectSuggestionMode = selectSuggestionMode;
SuggestionModeDescription = suggestionModeDescription;
SuggestionModeItem = suggestionModeItem;
- SuggestionIsEmpty = suggestionIsEmpty;
}
- public CompletionModel WithPresentedItems(IEnumerable<CompletionItemWithHighlight> newPresentedItems, int newSelectedIndex)
+ public CompletionModel WithPresentedItems(ImmutableArray<CompletionItemWithHighlight> newPresentedItems, int newSelectedIndex)
{
return new CompletionModel(
- originalItems: AllItems,
+ initialItems: InitialItems,
+ sortedItems: SortedItems,
applicableSpan: ApplicableSpan,
+ initialTriggerReason: InitialTriggerReason,
snapshot: Snapshot,
filters: Filters,
presentedItems: newPresentedItems, // Updated
useSoftSelection: UseSoftSelection,
- useSuggestionMode: UseSuggestionMode,
+ useSuggestionMode: DisplaySuggestionMode,
suggestionModeDescription: SuggestionModeDescription,
selectedIndex: newSelectedIndex, // Updated
selectSuggestionMode: SelectSuggestionMode,
suggestionModeItem: SuggestionModeItem,
- suggestionIsEmpty: SuggestionIsEmpty
+ uniqueItem: UniqueItem
);
}
public CompletionModel WithSnapshot(ITextSnapshot newSnapshot)
{
return new CompletionModel(
- originalItems: AllItems,
+ initialItems: InitialItems,
+ sortedItems: SortedItems,
applicableSpan: ApplicableSpan,
+ initialTriggerReason: InitialTriggerReason,
snapshot: newSnapshot, // Updated
filters: Filters,
presentedItems: PresentedItems,
useSoftSelection: UseSoftSelection,
- useSuggestionMode: UseSuggestionMode,
+ useSuggestionMode: DisplaySuggestionMode,
suggestionModeDescription: SuggestionModeDescription,
selectedIndex: SelectedIndex,
selectSuggestionMode: SelectSuggestionMode,
suggestionModeItem: SuggestionModeItem,
- suggestionIsEmpty: SuggestionIsEmpty
+ uniqueItem: UniqueItem
);
}
public CompletionModel WithFilters(ImmutableArray<CompletionFilterWithState> newFilters)
{
return new CompletionModel(
- originalItems: AllItems,
+ initialItems: InitialItems,
+ sortedItems: SortedItems,
applicableSpan: ApplicableSpan,
+ initialTriggerReason: InitialTriggerReason,
snapshot: Snapshot,
filters: newFilters, // Updated
presentedItems: PresentedItems,
useSoftSelection: UseSoftSelection,
- useSuggestionMode: UseSuggestionMode,
+ useSuggestionMode: DisplaySuggestionMode,
suggestionModeDescription: SuggestionModeDescription,
selectedIndex: SelectedIndex,
selectSuggestionMode: SelectSuggestionMode,
suggestionModeItem: SuggestionModeItem,
- suggestionIsEmpty: SuggestionIsEmpty
+ uniqueItem: UniqueItem
);
}
public CompletionModel WithSelectedIndex(int newIndex)
{
return new CompletionModel(
- originalItems: AllItems,
+ initialItems: InitialItems,
+ sortedItems: SortedItems,
applicableSpan: ApplicableSpan,
+ initialTriggerReason: InitialTriggerReason,
snapshot: Snapshot,
filters: Filters,
presentedItems: PresentedItems,
useSoftSelection: false, // Explicit selection and soft selection are mutually exclusive
- useSuggestionMode: UseSuggestionMode,
+ useSuggestionMode: DisplaySuggestionMode,
suggestionModeDescription: SuggestionModeDescription,
selectedIndex: newIndex, // Updated
selectSuggestionMode: false, // Explicit selection of regular item
suggestionModeItem: SuggestionModeItem,
- suggestionIsEmpty: SuggestionIsEmpty
+ uniqueItem: UniqueItem
);
}
public CompletionModel WithSuggestionItemSelected()
{
return new CompletionModel(
- originalItems: AllItems,
+ initialItems: InitialItems,
+ sortedItems: SortedItems,
applicableSpan: ApplicableSpan,
+ initialTriggerReason: InitialTriggerReason,
snapshot: Snapshot,
filters: Filters,
presentedItems: PresentedItems,
useSoftSelection: false, // Explicit selection and soft selection are mutually exclusive
- useSuggestionMode: UseSuggestionMode,
+ useSuggestionMode: DisplaySuggestionMode,
suggestionModeDescription: SuggestionModeDescription,
selectedIndex: -1, // Deselect regular item
selectSuggestionMode: true, // Explicit selection of suggestion item
suggestionModeItem: SuggestionModeItem,
- suggestionIsEmpty: SuggestionIsEmpty
+ uniqueItem: UniqueItem
+ );
+ }
+
+ public CompletionModel WithSuggestionModeActive(bool newUseSuggestionMode)
+ {
+ return new CompletionModel(
+ initialItems: InitialItems,
+ sortedItems: SortedItems,
+ applicableSpan: ApplicableSpan,
+ initialTriggerReason: InitialTriggerReason,
+ snapshot: Snapshot,
+ filters: Filters,
+ presentedItems: PresentedItems,
+ useSoftSelection: UseSoftSelection | newUseSuggestionMode, // Enabling suggestion mode also enables soft selection
+ useSuggestionMode: newUseSuggestionMode, // Updated
+ suggestionModeDescription: SuggestionModeDescription,
+ selectedIndex: SelectedIndex,
+ selectSuggestionMode: SelectSuggestionMode,
+ suggestionModeItem: SuggestionModeItem,
+ uniqueItem: UniqueItem
);
}
- internal CompletionModel WithSuggestionItem(CompletionItemWithHighlight newSuggestionModeItem)
+ /// <summary>
+ /// </summary>
+ /// <param name="newSuggestionModeItem">It is ok to pass in null when there is no suggestion. UI will display SuggestsionModeDescription instead.</param>
+ internal CompletionModel WithSuggestionModeItem(CompletionItem newSuggestionModeItem)
{
return new CompletionModel(
- originalItems: AllItems,
+ initialItems: InitialItems,
+ sortedItems: SortedItems,
applicableSpan: ApplicableSpan,
+ initialTriggerReason: InitialTriggerReason,
snapshot: Snapshot,
filters: Filters,
presentedItems: PresentedItems,
useSoftSelection: UseSoftSelection,
- useSuggestionMode: UseSuggestionMode,
+ useSuggestionMode: DisplaySuggestionMode,
suggestionModeDescription: SuggestionModeDescription,
selectedIndex: SelectedIndex,
selectSuggestionMode: SelectSuggestionMode,
- suggestionModeItem: newSuggestionModeItem, // Updated
- suggestionIsEmpty: false // This method guarantees that the suggestion is not empty
+ suggestionModeItem: newSuggestionModeItem,
+ uniqueItem: UniqueItem
);
}
- internal CompletionModel WithEmptySuggestionItem(CompletionItemWithHighlight newSuggestionModeItem)
+ /// <summary>
+ /// </summary>
+ /// <param name="newUniqueItem">Overrides typical unique item selection.
+ /// Pass in null to use regular behavior: treating single <see cref="PresentedItems"/> item as the unique item.</param>
+ internal CompletionModel WithUniqueItem(CompletionItem newUniqueItem)
{
return new CompletionModel(
- originalItems: AllItems,
+ initialItems: InitialItems,
+ sortedItems: SortedItems,
applicableSpan: ApplicableSpan,
+ initialTriggerReason: InitialTriggerReason,
snapshot: Snapshot,
filters: Filters,
presentedItems: PresentedItems,
useSoftSelection: UseSoftSelection,
- useSuggestionMode: UseSuggestionMode,
+ useSuggestionMode: DisplaySuggestionMode,
suggestionModeDescription: SuggestionModeDescription,
selectedIndex: SelectedIndex,
selectSuggestionMode: SelectSuggestionMode,
- suggestionModeItem: newSuggestionModeItem, // Updated
- suggestionIsEmpty: true // This method guarantees that the suggestion is empty
+ suggestionModeItem: SuggestionModeItem,
+ uniqueItem: newUniqueItem
+ );
+ }
+
+ internal CompletionModel WithSoftSelection(bool newSoftSelection)
+ {
+ return new CompletionModel(
+ initialItems: InitialItems,
+ sortedItems: SortedItems,
+ applicableSpan: ApplicableSpan,
+ initialTriggerReason: InitialTriggerReason,
+ snapshot: Snapshot,
+ filters: Filters,
+ presentedItems: PresentedItems,
+ useSoftSelection: newSoftSelection, // Updated
+ useSuggestionMode: DisplaySuggestionMode,
+ suggestionModeDescription: SuggestionModeDescription,
+ selectedIndex: SelectedIndex,
+ selectSuggestionMode: SelectSuggestionMode,
+ suggestionModeItem: SuggestionModeItem,
+ uniqueItem: UniqueItem
);
}
}
- class ModelComputation<TModel>
+ sealed class ModelComputation<TModel>
{
Task<TModel> _lastTask = Task.FromResult(default(TModel));
private Task _notifyUITask = Task.CompletedTask;
private readonly TaskScheduler _computationTaskScheduler;
private readonly CancellationToken _token;
- private readonly CancellationTokenSource _uiCancellation;
+ private CancellationTokenSource _uiCancellation;
+ internal TModel RecentModel { get; private set; } = default(TModel);
public ModelComputation(TaskScheduler computationTaskScheduler, CancellationToken token)
{
@@ -265,17 +347,26 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
/// </summary>
public void Enqueue(Func<TModel, CancellationToken, Task<TModel>> transformation, Func<TModel, Task> updateUI)
{
+ System.Diagnostics.Debug.WriteLine($"Enqueue runs on thread {Thread.CurrentThread.ManagedThreadId}");
// This method is based on Roslyn's ModelComputation.ChainTaskAndNotifyControllerWhenFinished
var nextTask = _lastTask.ContinueWith(t => transformation(t.Result, _token), _computationTaskScheduler).Unwrap();
_lastTask = nextTask;
+ // If the _notifyUITask is canceled, refresh it
+ if (_notifyUITask.IsCanceled || _uiCancellation.IsCancellationRequested)
+ {
+ _notifyUITask = Task.CompletedTask;
+ _uiCancellation = new CancellationTokenSource();
+ }
+
_notifyUITask = Task.Factory.ContinueWhenAll(
new[] { _notifyUITask, nextTask },
async existingTasks =>
{
if (existingTasks.All(t => t.Status == TaskStatus.RanToCompletion))
{
- if (nextTask == _lastTask && updateUI != null)
+ OnModelUpdated(nextTask.Result);
+ if (updateUI != null && nextTask == _lastTask)
{
await updateUI(nextTask.Result);
}
@@ -285,6 +376,12 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
);
}
+ private void OnModelUpdated(TModel result)
+ {
+ System.Diagnostics.Debug.WriteLine($"OnModelUpdated runs on thread {Thread.CurrentThread.ManagedThreadId}");
+ RecentModel = result;
+ }
+
/// <summary>
/// Blocks, waiting for all background work to finish.
/// Ignores the last piece of work a.k.a. "updateUI"
diff --git a/src/Language/Impl/Language/Completion/CompletionUtilities.cs b/src/Language/Impl/Language/Completion/CompletionUtilities.cs
index 613ff70..d6c9e0d 100644
--- a/src/Language/Impl/Language/Completion/CompletionUtilities.cs
+++ b/src/Language/Impl/Language/Completion/CompletionUtilities.cs
@@ -11,15 +11,14 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
{
internal static class CompletionUtilities
{
-
- internal static IEnumerable<ITextBuffer> GetBuffersForTriggerPoint(ITextView view, SnapshotPoint point)
+ internal static IEnumerable<ITextBuffer> GetBuffersForTriggerPoint(ITextView textView, SnapshotPoint point)
{
// We are looking at the buffer to the left of the caret.
- return view.BufferGraph.GetTextBuffers(n =>
- view.BufferGraph.MapDownToBuffer(point, PointTrackingMode.Negative, n, PositionAffinity.Predecessor) != null);
+ return textView.BufferGraph.GetTextBuffers(n =>
+ textView.BufferGraph.MapDownToBuffer(point, PointTrackingMode.Negative, n, PositionAffinity.Predecessor) != null);
}
- internal static IDictionary<IAsyncCompletionItemSource, SnapshotPoint> GetCompletionSourcesWithMappedLocations(ITextView view, SnapshotPoint originalPoint, Func<IContentType, ImmutableArray<IAsyncCompletionItemSource>> getCompletionItemSources)
+ internal static IDictionary<IAsyncCompletionItemSource, SnapshotPoint> GetCompletionSourcesWithMappedLocations(ITextView textView, SnapshotPoint originalPoint, Func<IContentType, ImmutableArray<IAsyncCompletionItemSource>> getCompletionItemSources)
{
// This method is created based on EditorCommandHandlerService.GetOrderedBuffersAndCommandHandlers
@@ -50,7 +49,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
var sortedContentTypes = new SortedSet<IContentType>(ContentTypeComparer.Instance);
var result = new Dictionary<IAsyncCompletionItemSource, SnapshotPoint>();
- var mappedPoints = GetPointsOnAvailableBuffers(view, originalPoint);
+ var mappedPoints = GetPointsOnAvailableBuffers(textView, originalPoint);
foreach (var mappedPoint in mappedPoints)
{
AddContentTypeHierarchy(sortedContentTypes, mappedPoint.Snapshot.ContentType);
@@ -73,10 +72,10 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
return result;
}
- private static IEnumerable<SnapshotPoint> GetPointsOnAvailableBuffers(ITextView view, SnapshotPoint point)
+ private static IEnumerable<SnapshotPoint> GetPointsOnAvailableBuffers(ITextView textView, SnapshotPoint point)
{
- var mappingPoint = view.BufferGraph.CreateMappingPoint(point, PointTrackingMode.Negative);
- var buffers = view.BufferGraph.GetTextBuffers(b => mappingPoint.GetPoint(b, PositionAffinity.Predecessor) != null);
+ var mappingPoint = textView.BufferGraph.CreateMappingPoint(point, PointTrackingMode.Negative);
+ var buffers = textView.BufferGraph.GetTextBuffers(b => mappingPoint.GetPoint(b, PositionAffinity.Predecessor) != null);
var pointsInBuffers = buffers.Select(b => mappingPoint.GetPoint(b, PositionAffinity.Predecessor).Value);
return pointsInBuffers;
}
diff --git a/src/Language/Impl/Language/Completion/DefaultCompletionService.cs b/src/Language/Impl/Language/Completion/DefaultCompletionService.cs
index 90a66ec..3e02dbd 100644
--- a/src/Language/Impl/Language/Completion/DefaultCompletionService.cs
+++ b/src/Language/Impl/Language/Completion/DefaultCompletionService.cs
@@ -1,48 +1,43 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.ComponentModel.Composition;
using System.Linq;
+using System.Threading;
using System.Threading.Tasks;
-using Microsoft.VisualStudio.Language.Intellisense;
-using System.Collections.Immutable;
+using Microsoft.VisualStudio.Core.Imaging;
using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.PatternMatching;
using Microsoft.VisualStudio.Utilities;
-using Microsoft.VisualStudio.Core.Imaging;
-using System;
-
-#if NET46
-using System.ComponentModel.Composition;
-#else
-using System.Composition;
-using Microsoft.VisualStudio.Text.Editor;
-#endif
namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
{
[Export(typeof(IAsyncCompletionService))]
- [Name("Default completion service")]
- [ContentType("text")]
- public class DefaultCompletionService : IAsyncCompletionService
+ [Name(KnownCompletionNames.DefaultCompletionService)]
+ [ContentType("any")]
+ internal class DefaultCompletionService : IAsyncCompletionService
{
[Import]
public IPatternMatcherFactory PatternMatcherFactory { get; set; }
-#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
- async Task<CompletionList> IAsyncCompletionService.UpdateCompletionListAsync(IEnumerable<CompletionItem> originalList, CompletionTrigger trigger, ITextSnapshot snapshot, ITrackingSpan applicableSpan, ImmutableArray<CompletionFilterWithState> filters)
-#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
+ Task<FilteredCompletionModel> IAsyncCompletionService.UpdateCompletionListAsync(
+ ImmutableArray<CompletionItem> sortedList, CompletionTriggerReason triggerReason, CompletionFilterReason filterReason,
+ ITextSnapshot snapshot, ITrackingSpan applicableSpan, ImmutableArray<CompletionFilterWithState> filters, ITextView view, CancellationToken token)
{
// Filter by text
var filterText = applicableSpan.GetText(snapshot);
if (string.IsNullOrWhiteSpace(filterText))
{
// There is no text filtering. Just apply user filters, sort alphabetically and return.
- var listFiltered = originalList;
+ IEnumerable<CompletionItem> listFiltered = sortedList;
if (filters.Any(n => n.IsSelected))
{
- listFiltered = originalList.Where(n => ShouldBeInCompletionList(n, filters));
+ listFiltered = sortedList.Where(n => ShouldBeInCompletionList(n, filters));
}
var listSorted = listFiltered.OrderBy(n => n.SortText);
- var listHighlighted = listSorted.Select(n => new CompletionItemWithHighlight(n));
- return new CompletionList(listHighlighted, 0, filters);
+ var listHighlighted = listSorted.Select(n => new CompletionItemWithHighlight(n)).ToImmutableArray();
+ return Task.FromResult(new FilteredCompletionModel(listHighlighted, 0, filters));
}
// Pattern matcher not only filters, but also provides a way to order the results by their match quality.
@@ -51,7 +46,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
filterText,
new PatternMatcherCreationOptions(System.Globalization.CultureInfo.CurrentCulture, PatternMatcherCreationFlags.IncludeMatchedSpans));
- var matches = originalList
+ var matches = sortedList
// Perform pattern matching
.Select(completionItem => (completionItem, patternMatcher.TryMatch(completionItem.FilterText)))
// Pick only items that were matched, unless length of filter text is 1
@@ -70,10 +65,8 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
filterFilteredList = matches.Where(n => ShouldBeInCompletionList(n.Item1, filters));
}
- // Order the list alphabetically and select the best match
- var sortedList = filterFilteredList.OrderBy(n => n.Item1.SortText);
var bestMatch = filterFilteredList.OrderByDescending(n => n.Item2.HasValue).ThenBy(n => n.Item2).FirstOrDefault();
- var listWithHighlights = sortedList.Select(n => n.Item2.HasValue ? new CompletionItemWithHighlight(n.Item1, n.Item2.Value.MatchedSpans) : new CompletionItemWithHighlight(n.Item1)).ToImmutableArray();
+ var listWithHighlights = filterFilteredList.Select(n => n.Item2.HasValue ? new CompletionItemWithHighlight(n.Item1, n.Item2.Value.MatchedSpans) : new CompletionItemWithHighlight(n.Item1)).ToImmutableArray();
int selectedItemIndex = 0;
for (int i = 0; i < listWithHighlights.Length; i++)
@@ -85,12 +78,19 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
}
}
- return new CompletionList(listWithHighlights, selectedItemIndex, updatedFilters);
+ return Task.FromResult(new FilteredCompletionModel(listWithHighlights, selectedItemIndex, updatedFilters));
+ }
+
+ Task<ImmutableArray<CompletionItem>> IAsyncCompletionService.SortCompletionListAsync(
+ ImmutableArray<CompletionItem> initialList, CompletionTriggerReason triggerReason, ITextSnapshot snapshot,
+ ITrackingSpan applicableToSpan, ITextView view, CancellationToken token)
+ {
+ return Task.FromResult(initialList.OrderBy(n => n.SortText).ToImmutableArray());
}
#region Filtering
- public static bool ShouldBeInCompletionList(
+ private static bool ShouldBeInCompletionList(
CompletionItem item,
ImmutableArray<CompletionFilterWithState> filtersWithState)
{
@@ -107,7 +107,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
#endregion
}
-#if DEBUG
+#if DEBUG && false
[Export(typeof(IAsyncCompletionItemSource))]
[Name("Debug completion item source")]
[Order(After = "default")]
@@ -123,18 +123,18 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
private static readonly ImmutableArray<CompletionFilter> FilterCollection1 = ImmutableArray.Create(Filter1);
private static readonly ImmutableArray<CompletionFilter> FilterCollection2 = ImmutableArray.Create(Filter2);
private static readonly ImmutableArray<CompletionFilter> FilterCollection3 = ImmutableArray.Create(Filter3);
- private static readonly ImmutableArray<string> commitCharacters = ImmutableArray.Create(" ", ";", "\t", ".", "<", "(", "[");
+ private static readonly ImmutableArray<char> commitCharacters = ImmutableArray.Create(' ', ';', '\t', '.', '<', '(', '[');
- void IAsyncCompletionItemSource.CustomCommit(Text.Editor.ITextView view, ITextBuffer buffer, CompletionItem item, ITrackingSpan applicableSpan, string commitCharacter)
+ void IAsyncCompletionItemSource.CustomCommit(Text.Editor.ITextView view, ITextBuffer buffer, CompletionItem item, ITrackingSpan applicableSpan, char typeChar)
{
throw new System.NotImplementedException();
}
- async Task<CompletionContext> IAsyncCompletionItemSource.GetCompletionContextAsync(CompletionTrigger trigger, SnapshotPoint triggerLocation)
+ async Task<CompletionContext> IAsyncCompletionItemSource.GetCompletionContextAsync(CompletionTrigger trigger, SnapshotPoint triggerLocation, CancellationToken token)
{
var charBeforeCaret = triggerLocation.Subtract(1).GetChar();
SnapshotSpan applicableSpan;
- if (commitCharacters.Contains(charBeforeCaret.ToString()))
+ if (commitCharacters.Contains(charBeforeCaret))
{
// skip this character. the applicable span starts later
applicableSpan = new SnapshotSpan(triggerLocation, 0);
@@ -146,32 +146,25 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
}
return await Task.FromResult(new CompletionContext(
ImmutableArray.Create(
- new CompletionItem("SampleItem<>", "SampleItem", "SampleItem<>", "SampleItem", this, FilterCollection1, false, Icon1),
- new CompletionItem("AnotherItem🐱‍👤", "AnotherItem", "AnotherItem", "AnotherItem", this, FilterCollection1, false, Icon1),
- new CompletionItem("Aaaaa", "Aaaaa", "Aaaaa", "Aaaaa", this, FilterCollection2, false, Icon2),
- new CompletionItem("Bbbbb", "Bbbbb", "Bbbbb", "Bbbbb", this, FilterCollection2, false, Icon2),
- new CompletionItem("Ccccc", "Ccccc", "Ccccc", "Ccccc", this, FilterCollection2, false, Icon2),
- new CompletionItem("Ddddd", "Ddddd", "Ddddd", "Ddddd", this, FilterCollection2, false, Icon2),
- new CompletionItem("Eeee", "Eeee", "Eeee", "Eeee", this, FilterCollection2, false, Icon2),
- new CompletionItem("Ffffff", "Ffffff", "Ffffff", "Ffffff", this, FilterCollection2, false, Icon2),
- new CompletionItem("Ggggggg", "Ggggggg", "Ggggggg", "Ggggggg", this, FilterCollection2, false, Icon2),
- new CompletionItem("Hhhhh", "Hhhhh", "Hhhhh", "Hhhhh", this, FilterCollection2, false, Icon2),
- new CompletionItem("Iiiii", "Iiiii", "Iiiii", "Iiiii", this, FilterCollection2, false, Icon2),
- new CompletionItem("Jjjjj", "Jjjjj", "Jjjjj", "Jjjjj", this, FilterCollection3, false, Icon3),
- new CompletionItem("kkkkk", "kkkkk", "kkkkk", "kkkkk", this, FilterCollection3, false, Icon3),
- new CompletionItem("llllol", "llllol", "llllol", "llllol", this, FilterCollection3, false, Icon3),
- new CompletionItem("mmmmm", "mmmmm", "mmmmm", "mmmmm", this, FilterCollection3, false, Icon3),
- new CompletionItem("nnNnnn", "nnNnnn", "nnNnnn", "nnNnnn", this, FilterCollection3, false, Icon3),
- new CompletionItem("oOoOOO", "oOoOOO", "oOoOOO", "oOoOOO", this, FilterCollection3, false, Icon3)
+ new CompletionItem("SampleItem<>", this, Icon3, FilterCollection3, string.Empty, false, "SampleItem", "SampleItem<>", "SampleItem", ImmutableArray<AccessibleImage>.Empty),
+ new CompletionItem("AnotherItem🐱‍👤", this, Icon3, FilterCollection3, string.Empty, false, "AnotherItem", "AnotherItem", "AnotherItem", ImmutableArray.Create(new AccessibleImage("cat", "ninja cat", Icon3))),
+ new CompletionItem("Sampling", this, Icon1, FilterCollection1),
+ new CompletionItem("Sampler", this, Icon1, FilterCollection1),
+ new CompletionItem("Sapling", this, Icon2, FilterCollection2, "Sapling is a young tree"),
+ new CompletionItem("OverSampling", this, Icon1, FilterCollection1, "overload"),
+ new CompletionItem("AnotherSample", this, Icon2, FilterCollection2),
+ new CompletionItem("AnotherSampling", this, Icon2, FilterCollection2),
+ new CompletionItem("Simple", this, Icon3, FilterCollection3, "KISS"),
+ new CompletionItem("Simpler", this, Icon3, FilterCollection3, "KISS")
), applicableSpan));//, true, true, "Suggestion mode description!"));
}
- async Task<object> IAsyncCompletionItemSource.GetDescriptionAsync(CompletionItem item)
+ async Task<object> IAsyncCompletionItemSource.GetDescriptionAsync(CompletionItem item, CancellationToken token)
{
return await Task.FromResult("This is a tooltip for " + item.DisplayText);
}
- ImmutableArray<string> IAsyncCompletionItemSource.GetPotentialCommitCharacters() => commitCharacters;
+ ImmutableArray<char> IAsyncCompletionItemSource.GetPotentialCommitCharacters() => commitCharacters;
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
async Task IAsyncCompletionItemSource.HandleViewClosedAsync(Text.Editor.ITextView view)
@@ -180,12 +173,12 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
return;
}
- bool IAsyncCompletionItemSource.ShouldCommitCompletion(string typedChar, SnapshotPoint location)
+ bool IAsyncCompletionItemSource.ShouldCommitCompletion(char typeChar, SnapshotPoint location)
{
return true;
}
- bool IAsyncCompletionItemSource.ShouldTriggerCompletion(string typedChar, SnapshotPoint location)
+ bool IAsyncCompletionItemSource.ShouldTriggerCompletion(char typeChar, SnapshotPoint location)
{
return true;
}
@@ -197,24 +190,24 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
[ContentType("RazorCSharp")]
public class DebugHtmlCompletionItemSource : IAsyncCompletionItemSource
{
- void IAsyncCompletionItemSource.CustomCommit(Text.Editor.ITextView view, ITextBuffer buffer, CompletionItem item, ITrackingSpan applicableSpan, string commitCharacter)
+ void IAsyncCompletionItemSource.CustomCommit(Text.Editor.ITextView view, ITextBuffer buffer, CompletionItem item, ITrackingSpan applicableSpan, char typeChar)
{
throw new System.NotImplementedException();
}
- async Task<CompletionContext> IAsyncCompletionItemSource.GetCompletionContextAsync(CompletionTrigger trigger, SnapshotPoint triggerLocation)
+ async Task<CompletionContext> IAsyncCompletionItemSource.GetCompletionContextAsync(CompletionTrigger trigger, SnapshotPoint triggerLocation, 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)), new SnapshotSpan(triggerLocation, 0)));
}
- async Task<object> IAsyncCompletionItemSource.GetDescriptionAsync(CompletionItem item)
+ async Task<object> IAsyncCompletionItemSource.GetDescriptionAsync(CompletionItem item, CancellationToken token)
{
return await Task.FromResult(item.DisplayText);
}
- ImmutableArray<string> IAsyncCompletionItemSource.GetPotentialCommitCharacters()
+ ImmutableArray<char> IAsyncCompletionItemSource.GetPotentialCommitCharacters()
{
- return ImmutableArray.Create(" ", ">", "=", "\t");
+ return ImmutableArray.Create(' ', '>', '=', '\t');
}
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
@@ -224,12 +217,12 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
return;
}
- bool IAsyncCompletionItemSource.ShouldCommitCompletion(string typedChar, SnapshotPoint location)
+ bool IAsyncCompletionItemSource.ShouldCommitCompletion(char typeChar, SnapshotPoint location)
{
return true;
}
- bool IAsyncCompletionItemSource.ShouldTriggerCompletion(string typedChar, SnapshotPoint location)
+ bool IAsyncCompletionItemSource.ShouldTriggerCompletion(char typeChar, SnapshotPoint location)
{
return true;
}
diff --git a/src/Language/Impl/Language/Completion/ModernCompletionFeature.cs b/src/Language/Impl/Language/Completion/ModernCompletionFeature.cs
new file mode 100644
index 0000000..4a3f874
--- /dev/null
+++ b/src/Language/Impl/Language/Completion/ModernCompletionFeature.cs
@@ -0,0 +1,32 @@
+using System.Diagnostics;
+using Microsoft.VisualStudio.Text.Utilities;
+
+namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
+{
+ /// <summary>
+ /// Provides information whether modern completion should be enabled, given the buffer's content type.
+ /// </summary>
+ internal static class ModernCompletionFeature
+ {
+ private const string TreatmentFlightName = "CompletionAPI";
+ private static bool _treatmentFlightEnabled;
+ private static bool _initialized;
+
+ /// <summary>
+ /// Returns whether or not modern completion should be enabled.
+ /// </summary>
+ /// <returns>true if experiment is enabled.</returns>
+ public static bool GetFeatureState(IExperimentationServiceInternal experimentationService)
+ {
+ if (_initialized)
+ {
+ return _treatmentFlightEnabled;
+ }
+
+ _treatmentFlightEnabled = experimentationService.IsCachedFlightEnabled(TreatmentFlightName);
+ Debug.Assert(_treatmentFlightEnabled);
+ _initialized = true;
+ return _treatmentFlightEnabled;
+ }
+ }
+}
diff --git a/src/Language/Impl/Language/Completion/SelectDownCommandHandler.cs b/src/Language/Impl/Language/Completion/SelectDownCommandHandler.cs
deleted file mode 100644
index 6d38a74..0000000
--- a/src/Language/Impl/Language/Completion/SelectDownCommandHandler.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using System.ComponentModel.Composition;
-using Microsoft.VisualStudio.Commanding;
-using Microsoft.VisualStudio.Language.Intellisense;
-using Microsoft.VisualStudio.Text.Editor.Commanding.Commands;
-using Microsoft.VisualStudio.Utilities;
-
-namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
-{
- /// <summary>
- /// Reacts to the down arrow command and attempts to scroll the completion list.
- /// </summary>
- [Name(nameof(SelectDownCommandHandler))]
- [ContentType("any")]
- [Export(typeof(ICommandHandler))]
- internal sealed class SelectDownCommandHandler : ICommandHandler<DownKeyCommandArgs>
- {
- [Import]
- IAsyncCompletionBroker broker;
-
- // TODO: Localize
- string INamed.DisplayName => "Handler for down arrow in completion";
-
- bool ICommandHandler<DownKeyCommandArgs>.ExecuteCommand(DownKeyCommandArgs args, CommandExecutionContext executionContext)
- {
- if (broker.IsCompletionActive(args.TextView))
- {
- broker.SelectDown(args.TextView);
- return true;
- }
- return false;
- }
-
- CommandState ICommandHandler<DownKeyCommandArgs>.GetCommandState(DownKeyCommandArgs args)
- {
- return broker.IsCompletionActive(args.TextView)
- ? CommandState.Available
- : CommandState.Unspecified;
- }
- }
-}
diff --git a/src/Language/Impl/Language/Completion/SelectPageDownCommandHandler.cs b/src/Language/Impl/Language/Completion/SelectPageDownCommandHandler.cs
deleted file mode 100644
index c9b0cda..0000000
--- a/src/Language/Impl/Language/Completion/SelectPageDownCommandHandler.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using System.ComponentModel.Composition;
-using Microsoft.VisualStudio.Commanding;
-using Microsoft.VisualStudio.Language.Intellisense;
-using Microsoft.VisualStudio.Text.Editor.Commanding.Commands;
-using Microsoft.VisualStudio.Utilities;
-
-namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
-{
- /// <summary>
- /// Reacts to the page down command and attempts to scroll the completion list.
- /// </summary>
- [Name(nameof(SelectPageDownCommandHandler))]
- [ContentType("any")]
- [Export(typeof(ICommandHandler))]
- internal sealed class SelectPageDownCommandHandler : ICommandHandler<PageDownKeyCommandArgs>
- {
- [Import]
- IAsyncCompletionBroker broker;
-
- // TODO: Localize
- string INamed.DisplayName => "Handler for page down in completion";
-
- bool ICommandHandler<PageDownKeyCommandArgs>.ExecuteCommand(PageDownKeyCommandArgs args, CommandExecutionContext executionContext)
- {
- if (broker.IsCompletionActive(args.TextView))
- {
- broker.SelectPageDown(args.TextView);
- return true;
- }
- return false;
- }
-
- CommandState ICommandHandler<PageDownKeyCommandArgs>.GetCommandState(PageDownKeyCommandArgs args)
- {
- return broker.IsCompletionActive(args.TextView)
- ? CommandState.Available
- : CommandState.Unspecified;
- }
- }
-}
diff --git a/src/Language/Impl/Language/Completion/SelectPageUpCommandHandler.cs b/src/Language/Impl/Language/Completion/SelectPageUpCommandHandler.cs
deleted file mode 100644
index 49af033..0000000
--- a/src/Language/Impl/Language/Completion/SelectPageUpCommandHandler.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using System.ComponentModel.Composition;
-using Microsoft.VisualStudio.Commanding;
-using Microsoft.VisualStudio.Language.Intellisense;
-using Microsoft.VisualStudio.Text.Editor.Commanding.Commands;
-using Microsoft.VisualStudio.Utilities;
-
-namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
-{
- /// <summary>
- /// Reacts to the page up command and attempts to scroll the completion list.
- /// </summary>
- [Name(nameof(SelectPageUpCommandHandler))]
- [ContentType("any")]
- [Export(typeof(ICommandHandler))]
- internal sealed class SelectPageUpCommandHandler : ICommandHandler<PageUpKeyCommandArgs>
- {
- [Import]
- IAsyncCompletionBroker broker;
-
- // TODO: Localize
- string INamed.DisplayName => "Handler for page down in completion";
-
- bool ICommandHandler<PageUpKeyCommandArgs>.ExecuteCommand(PageUpKeyCommandArgs args, CommandExecutionContext executionContext)
- {
- if (broker.IsCompletionActive(args.TextView))
- {
- broker.SelectPageUp(args.TextView);
- return true;
- }
- return false;
- }
-
- CommandState ICommandHandler<PageUpKeyCommandArgs>.GetCommandState(PageUpKeyCommandArgs args)
- {
- return broker.IsCompletionActive(args.TextView)
- ? CommandState.Available
- : CommandState.Unspecified;
- }
- }
-}
diff --git a/src/Language/Impl/Language/Completion/SelectUpCommandHandler.cs b/src/Language/Impl/Language/Completion/SelectUpCommandHandler.cs
deleted file mode 100644
index d31e71e..0000000
--- a/src/Language/Impl/Language/Completion/SelectUpCommandHandler.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using System.ComponentModel.Composition;
-using Microsoft.VisualStudio.Commanding;
-using Microsoft.VisualStudio.Language.Intellisense;
-using Microsoft.VisualStudio.Text.Editor.Commanding.Commands;
-using Microsoft.VisualStudio.Utilities;
-
-namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
-{
- /// <summary>
- /// Reacts to the up arrow command and attempts to scroll the completion list.
- /// </summary>
- [Name(nameof(SelectUpCommandHandler))]
- [ContentType("any")]
- [Export(typeof(ICommandHandler))]
- internal sealed class SelectUpCommandHandler : ICommandHandler<UpKeyCommandArgs>
- {
- [Import]
- IAsyncCompletionBroker broker;
-
- // TODO: Localize
- string INamed.DisplayName => "Handler for down arrow in completion";
-
- bool ICommandHandler<UpKeyCommandArgs>.ExecuteCommand(UpKeyCommandArgs args, CommandExecutionContext executionContext)
- {
- if (broker.IsCompletionActive(args.TextView))
- {
- broker.SelectUp(args.TextView);
- return true;
- }
- return false;
- }
-
- CommandState ICommandHandler<UpKeyCommandArgs>.GetCommandState(UpKeyCommandArgs args)
- {
- return broker.IsCompletionActive(args.TextView)
- ? CommandState.Available
- : CommandState.Unspecified;
- }
- }
-}
diff --git a/src/Language/Impl/Language/Completion/SuggestionModeCompletionItemSource.cs b/src/Language/Impl/Language/Completion/SuggestionModeCompletionItemSource.cs
new file mode 100644
index 0000000..e3a6abb
--- /dev/null
+++ b/src/Language/Impl/Language/Completion/SuggestionModeCompletionItemSource.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections.Immutable;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
+
+namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
+{
+ /// <summary>
+ /// Internal item source used during lifetime of the suggestion mode item.
+ /// </summary>
+ internal class SuggestionModeCompletionItemSource : IAsyncCompletionItemSource
+ {
+ static IAsyncCompletionItemSource _instance;
+ internal static IAsyncCompletionItemSource Instance
+ {
+ get
+ {
+ if (_instance == null)
+ _instance = new SuggestionModeCompletionItemSource();
+ return _instance;
+ }
+ }
+
+ void IAsyncCompletionItemSource.CustomCommit(ITextView view, ITextBuffer buffer, CompletionItem item, ITrackingSpan applicableSpan, char typeChar, CancellationToken token)
+ {
+ throw new NotImplementedException("Suggestion mode item does not have custom commit behavior");
+ }
+
+ Task<CompletionContext> IAsyncCompletionItemSource.GetCompletionContextAsync(CompletionTrigger trigger, SnapshotPoint triggerLocation, CancellationToken token)
+ {
+ throw new NotImplementedException("This item source is not meant to be registered. It is used only to provide tooltip.");
+ }
+
+ Task<object> IAsyncCompletionItemSource.GetDescriptionAsync(CompletionItem item, CancellationToken token)
+ {
+ return Task.FromResult<object>(string.Empty);
+ }
+
+ ImmutableArray<char> IAsyncCompletionItemSource.GetPotentialCommitCharacters()
+ {
+ throw new NotImplementedException("This item source is not meant to be registered. It is used only to provide tooltip.");
+ }
+
+ Task IAsyncCompletionItemSource.HandleViewClosedAsync(ITextView view)
+ {
+ throw new NotImplementedException("This item source is not meant to be registered. It is used only to provide tooltip.");
+ }
+
+ bool IAsyncCompletionItemSource.ShouldCommitCompletion(char typeChar, SnapshotPoint location)
+ {
+ return false; // Typing should not commit the suggestion mode item.
+ }
+
+ bool IAsyncCompletionItemSource.ShouldTriggerCompletion(char typeChar, SnapshotPoint location)
+ {
+ throw new NotImplementedException("This item source is not meant to be registered. It is used only to provide tooltip.");
+ }
+ }
+}
diff --git a/src/Language/Impl/Language/Strings.Designer.cs b/src/Language/Impl/Language/Strings.Designer.cs
new file mode 100644
index 0000000..e4e63cc
--- /dev/null
+++ b/src/Language/Impl/Language/Strings.Designer.cs
@@ -0,0 +1,72 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace Microsoft.VisualStudio.Language.Intellisense.Implementation {
+ using System;
+
+
+ /// <summary>
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ /// </summary>
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ public class Strings {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Strings() {
+ }
+
+ /// <summary>
+ /// Returns the cached ResourceManager instance used by this class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.VisualStudio.Text.Implementation.Language.Impl.Language.Strings", typeof(Strings).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ /// <summary>
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Completion command handler.
+ /// </summary>
+ public static string CompletionCommandHandlerName {
+ get {
+ return ResourceManager.GetString("CompletionCommandHandlerName", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/src/Language/Impl/Language/Strings.resx b/src/Language/Impl/Language/Strings.resx
new file mode 100644
index 0000000..3870d03
--- /dev/null
+++ b/src/Language/Impl/Language/Strings.resx
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <data name="CompletionCommandHandlerName" xml:space="preserve">
+ <value>Completion command handler</value>
+ </data>
+</root> \ No newline at end of file
diff --git a/src/Microsoft.VisualStudio.Text.Implementation.csproj b/src/Microsoft.VisualStudio.Text.Implementation.csproj
index adf7d62..d2e464e 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.13-pre</Version>
+ <Version>15.0.14-pre</Version>
<AssemblyVersion>15.0.0.0</AssemblyVersion>
- <NuGetVersionEditor>15.8.173-preview-g3a2638b5fc</NuGetVersionEditor>
- <NuGetVersionLanguage>15.6.289-preview-g5a23388e08</NuGetVersionLanguage>
+ <NuGetVersionEditor>15.6.391-preview-g3c4ef6e5fc</NuGetVersionEditor>
+ <NuGetVersionLanguage>15.6.391-preview-g3c4ef6e5fc</NuGetVersionLanguage>
</PropertyGroup>
<PropertyGroup>
@@ -33,6 +33,11 @@
<DesignTime>True</DesignTime>
<DependentUpon>Strings.resx</DependentUpon>
</Compile>
+ <Compile Update="Language\Impl\Language\Strings.Designer.cs">
+ <AutoGen>True</AutoGen>
+ <DesignTime>True</DesignTime>
+ <DependentUpon>Strings.resx</DependentUpon>
+ </Compile>
<Compile Update="Text\Impl\TextModel\Strings.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
@@ -76,6 +81,11 @@
<LastGenOutput>Strings.Designer.cs</LastGenOutput>
<CustomToolNamespace>Microsoft.VisualStudio.Utilities.Implementation</CustomToolNamespace>
</EmbeddedResource>
+ <EmbeddedResource Update="Language\Impl\Language\Strings.resx">
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>Strings.Designer.cs</LastGenOutput>
+ <CustomToolNamespace>Microsoft.VisualStudio.Language.Intellisense.Implementation</CustomToolNamespace>
+ </EmbeddedResource>
<EmbeddedResource Update="Text\Impl\TextModel\Strings.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Strings.Designer.cs</LastGenOutput>
diff --git a/src/Text/Def/TextData/Model/IExtensionPerformanceTracker.cs b/src/Text/Def/TextData/Model/IExtensionPerformanceTracker.cs
index fae97b8..3c5abdd 100644
--- a/src/Text/Def/TextData/Model/IExtensionPerformanceTracker.cs
+++ b/src/Text/Def/TextData/Model/IExtensionPerformanceTracker.cs
@@ -26,5 +26,15 @@ namespace Microsoft.VisualStudio.Text
/// Invoked after calling to an extension event handler.
/// </summary>
void AfterCallingEventHandler(Delegate eventHandler);
+
+ /// <summary>
+ /// Invoked before calling to an extensibility point.
+ /// </summary>
+ void BeforeCallingExtension(object extension);
+
+ /// <summary>
+ /// Invoked after calling to an extensibility point.
+ /// </summary>
+ void AfterCallingExtension(object extension);
}
}
diff --git a/src/Text/Def/TextUI/Utilities/AbstractUIThreadOperationContext.cs b/src/Text/Def/TextUI/Utilities/AbstractUIThreadOperationContext.cs
index c7d8279..8047106 100644
--- a/src/Text/Def/TextUI/Utilities/AbstractUIThreadOperationContext.cs
+++ b/src/Text/Def/TextUI/Utilities/AbstractUIThreadOperationContext.cs
@@ -13,7 +13,7 @@ namespace Microsoft.VisualStudio.Utilities
private List<IUIThreadOperationScope> _scopes;
private bool _allowCancellation;
private PropertyCollection _properties;
- private readonly string _contextDescription;
+ private readonly string _defaultDescription;
private int _completedItems;
private int _totalItems;
@@ -22,11 +22,11 @@ namespace Microsoft.VisualStudio.Utilities
/// </summary>
/// <param name="allowCancellation">Initial value of the <see cref="IUIThreadOperationContext.AllowCancellation"/>
/// property, which can change as new scopes are added to the context.</param>
- /// <param name="description">Initial value of the <see cref="IUIThreadOperationContext.Description"/>
+ /// <param name="defaultDescription">Default value of the <see cref="IUIThreadOperationContext.Description"/>
/// property, which can change as new scopes are added to the context.</param>
- public AbstractUIThreadOperationContext(bool allowCancellation, string description)
+ public AbstractUIThreadOperationContext(bool allowCancellation, string defaultDescription)
{
- _contextDescription = description ?? throw new ArgumentNullException(nameof(description));
+ _defaultDescription = defaultDescription ?? throw new ArgumentNullException(nameof(defaultDescription));
_allowCancellation = allowCancellation;
}
@@ -64,7 +64,7 @@ namespace Microsoft.VisualStudio.Utilities
}
/// <summary>
- /// Gets user readable operation description, composed of initial context description and
+ /// Gets user readable operation description, composed of initial context description or
/// descriptions of all currently added scopes.
/// </summary>
public virtual string Description
@@ -73,11 +73,17 @@ namespace Microsoft.VisualStudio.Utilities
{
if (_scopes == null || _scopes.Count == 0)
{
- return _contextDescription;
+ return _defaultDescription;
}
- // Combine context description with descriptions of all current scopes
- return _contextDescription + Environment.NewLine + string.Join(Environment.NewLine, _scopes.Select((s) => s.Description));
+ // Most common case
+ if (_scopes.Count == 1)
+ {
+ return _scopes[0].Description;
+ }
+
+ // Combine descriptions of all current scopes
+ return string.Join(Environment.NewLine, _scopes.Select((s) => s.Description));
}
}
diff --git a/src/Text/Def/TextUI/Utilities/IUIThreadOperationExecutor.cs b/src/Text/Def/TextUI/Utilities/IUIThreadOperationExecutor.cs
index 3daf364..b47b0b4 100644
--- a/src/Text/Def/TextUI/Utilities/IUIThreadOperationExecutor.cs
+++ b/src/Text/Def/TextUI/Utilities/IUIThreadOperationExecutor.cs
@@ -65,12 +65,14 @@ namespace Microsoft.VisualStudio.Utilities
/// Executes the action synchronously and waits for it to complete.
/// </summary>
/// <param name="title">Operation's title.</param>
- /// <param name="description">Initial operation's description.</param>
- /// <param name="allowCancel">Whether to allow cancellability.</param>
+ /// <param name="defaultDescription">Default operation's description, which is displayed on the wait dialog unless
+ /// one or more <see cref="IUIThreadOperationScope"/>s with more specific descriptions were added to
+ /// the <see cref="IUIThreadOperationContext"/>.</param>
+ /// <param name="allowCancellation">Whether to allow cancellability.</param>
/// <param name="showProgress">Whether to show progress indication.</param>
/// <param name="action">An action to execute.</param>
/// <returns>A status of action execution.</returns>
- UIThreadOperationStatus Execute(string title, string description, bool allowCancel, bool showProgress,
+ UIThreadOperationStatus Execute(string title, string defaultDescription, bool allowCancellation, bool showProgress,
Action<IUIThreadOperationContext> action);
/// <summary>
@@ -78,12 +80,14 @@ namespace Microsoft.VisualStudio.Utilities
/// cancellability and wait indication.
/// </summary>
/// <param name="title">Operation's title.</param>
- /// <param name="description">Initial operation's description.</param>
- /// <param name="allowCancel">Whether to allow cancellability.</param>
+ /// <param name="defaultDescription">Default operation's description, which is displayed on the wait dialog unless
+ /// one or more <see cref="IUIThreadOperationScope"/>s with more specific descriptions were added to
+ /// the <see cref="IUIThreadOperationContext"/>.</param>
+ /// <param name="allowCancellation">Whether to allow cancellability.</param>
/// <param name="showProgress">Whether to show progress indication.</param>
/// <returns><see cref="IUIThreadOperationContext"/> instance that provides access to shared two way
/// cancellability and wait indication for the given operation. The operation is considered executed
/// when this <see cref="IUIThreadOperationContext"/> instance is disposed.</returns>
- IUIThreadOperationContext BeginExecute(string title, string description, bool allowCancel, bool showProgress);
+ IUIThreadOperationContext BeginExecute(string title, string defaultDescription, bool allowCancellation, bool showProgress);
}
}
diff --git a/src/Text/Impl/ClassificationAggregator/ClassifierAggregator.cs b/src/Text/Impl/ClassificationAggregator/ClassifierAggregator.cs
index 7ccd6cb..b10eee8 100644
--- a/src/Text/Impl/ClassificationAggregator/ClassifierAggregator.cs
+++ b/src/Text/Impl/ClassificationAggregator/ClassifierAggregator.cs
@@ -97,11 +97,17 @@ namespace Microsoft.VisualStudio.Text.Classification.Implementation
/// </returns>
public IList<ClassificationSpan> GetClassificationSpans(SnapshotSpan span)
{
+ if (_tagAggregator == null)
+ return new List<ClassificationSpan>(0);
+
return this.InternalGetClassificationSpans(span, _tagAggregator.GetTags(span));
}
public IList<ClassificationSpan> GetAllClassificationSpans(SnapshotSpan span, CancellationToken cancel)
{
+ if (_tagAggregator == null)
+ return new List<ClassificationSpan>(0);
+
return this.InternalGetClassificationSpans(span, _tagAggregator.GetAllTags(span, cancel));
}
#endregion // Exposed Methods
diff --git a/src/Text/Impl/Commanding/CommandingStrings.Designer.cs b/src/Text/Impl/Commanding/CommandingStrings.Designer.cs
index cfaadfd..714059e 100644
--- a/src/Text/Impl/Commanding/CommandingStrings.Designer.cs
+++ b/src/Text/Impl/Commanding/CommandingStrings.Designer.cs
@@ -61,16 +61,7 @@ namespace Microsoft.VisualStudio.UI.Text.Commanding.Implementation {
}
/// <summary>
- /// Looks up a localized string similar to Executing a command.
- /// </summary>
- internal static string ExecutingCommand {
- get {
- return ResourceManager.GetString("ExecutingCommand", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Please wait for command execution to finish....
+ /// Looks up a localized string similar to Please wait for an editor command to finish....
/// </summary>
internal static string WaitForCommandExecution {
get {
diff --git a/src/Text/Impl/Commanding/CommandingStrings.resx b/src/Text/Impl/Commanding/CommandingStrings.resx
index 72d604d..b05561d 100644
--- a/src/Text/Impl/Commanding/CommandingStrings.resx
+++ b/src/Text/Impl/Commanding/CommandingStrings.resx
@@ -98,10 +98,7 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
- <data name="ExecutingCommand" xml:space="preserve">
- <value>Executing a command</value>
- </data>
<data name="WaitForCommandExecution" xml:space="preserve">
- <value>Please wait for command execution to finish...</value>
+ <value>Please wait for an editor command to finish...</value>
</data>
</root> \ No newline at end of file
diff --git a/src/Text/Impl/Commanding/EditorCommandHandlerService.cs b/src/Text/Impl/Commanding/EditorCommandHandlerService.cs
index 3bd08c2..315e55f 100644
--- a/src/Text/Impl/Commanding/EditorCommandHandlerService.cs
+++ b/src/Text/Impl/Commanding/EditorCommandHandlerService.cs
@@ -25,6 +25,7 @@ namespace Microsoft.VisualStudio.UI.Text.Commanding.Implementation
private readonly static IReadOnlyList<ICommandHandlerAndMetadata> EmptyHandlerList = new List<ICommandHandlerAndMetadata>(0);
private readonly static Action EmptyAction = delegate { };
private readonly static Func<CommandState> UnavalableCommandFunc = new Func<CommandState>(() => CommandState.Unavailable);
+ private readonly static string WaitForCommandExecutionString = CommandingStrings.WaitForCommandExecution;
/// This dictionary acts as a cache so we can avoid having to look through the full list of
/// handlers every time we need handlers of a specific type, for a given content type.
@@ -128,7 +129,7 @@ namespace Microsoft.VisualStudio.UI.Text.Commanding.Implementation
commandExecutionContext = CreateCommandExecutionContext();
}
- handlerChain = () => handler.ExecuteCommand(args, nextHandler, commandExecutionContext);
+ handlerChain = () => _guardedOperations.CallExtensionPoint(handler, () => handler.ExecuteCommand(args, nextHandler, commandExecutionContext));
}
ExecuteCommandHandlerChain(commandExecutionContext, handlerChain, nextCommandHandler);
@@ -153,7 +154,7 @@ namespace Microsoft.VisualStudio.UI.Text.Commanding.Implementation
}
finally
{
- commandExecutionContext?.WaitContext?.Dispose();
+ commandExecutionContext?.OperationContext?.Dispose();
}
}
@@ -181,8 +182,8 @@ namespace Microsoft.VisualStudio.UI.Text.Commanding.Implementation
private CommandExecutionContext CreateCommandExecutionContext()
{
CommandExecutionContext commandExecutionContext;
- var uiThreadOperationContext = _uiThreadOperationExecutor.BeginExecute(CommandingStrings.ExecutingCommand,
- CommandingStrings.WaitForCommandExecution, allowCancel: true, showProgress: true);
+ var uiThreadOperationContext = _uiThreadOperationExecutor.BeginExecute(title: null, // We want same caption as the main window
+ defaultDescription: WaitForCommandExecutionString, allowCancellation: true, showProgress: true);
commandExecutionContext = new CommandExecutionContext(uiThreadOperationContext);
return commandExecutionContext;
}
diff --git a/src/Text/Impl/Outlining/OutliningManager.cs b/src/Text/Impl/Outlining/OutliningManager.cs
index ba20323..71418ec 100644
--- a/src/Text/Impl/Outlining/OutliningManager.cs
+++ b/src/Text/Impl/Outlining/OutliningManager.cs
@@ -413,7 +413,9 @@ namespace Microsoft.VisualStudio.Text.Outlining
{
// TODO: Notify providers somehow.
// Or rewrite so that such things are legal.
+#if false
Debug.WriteLine("IGNORING TAG " + spans[0] + " due to span conflict");
+#endif
}
else
{
@@ -422,7 +424,9 @@ namespace Microsoft.VisualStudio.Text.Outlining
}
else
{
+#if false
Debug.WriteLine("IGNORING TAG " + tagSpan.Span.GetSpans(editBuffer) + " because it was split or shortened by projection");
+#endif
}
}
@@ -493,9 +497,9 @@ namespace Microsoft.VisualStudio.Text.Outlining
return merged;
}
- #endregion
+#endregion
- #region Getting collapsibles
+#region Getting collapsibles
public IEnumerable<ICollapsed> GetCollapsedRegions(SnapshotSpan span)
{
@@ -635,17 +639,17 @@ namespace Microsoft.VisualStudio.Text.Outlining
!collapsedRegionTree.IsPointContainedInANode(regionSpan.End);
}
- #endregion
+#endregion
- #region IAccurateOutliningManager methods
+#region IAccurateOutliningManager methods
public IEnumerable<ICollapsed> CollapseAll(SnapshotSpan span, Predicate<ICollapsible> match, CancellationToken cancel)
{
return this.InternalCollapseAll(span, match, cancel: cancel);
}
- #endregion
+#endregion
- #region IDisposable
+#region IDisposable
public void Dispose()
{
@@ -705,10 +709,10 @@ namespace Microsoft.VisualStudio.Text.Outlining
}
}
- #endregion
+#endregion
}
- #region Sorter for sorted lists of collapsibles
+#region Sorter for sorted lists of collapsibles
class CollapsibleSorter : IComparer<ICollapsible>
{
private ITextBuffer SourceBuffer { get; set; }
@@ -737,5 +741,5 @@ namespace Microsoft.VisualStudio.Text.Outlining
return -left.Length.CompareTo(right.Length);
}
}
- #endregion
+#endregion
}
diff --git a/src/Text/Util/TextDataUtil/GuardedOperations.cs b/src/Text/Util/TextDataUtil/GuardedOperations.cs
index 8cc4415..4a9ff53 100644
--- a/src/Text/Util/TextDataUtil/GuardedOperations.cs
+++ b/src/Text/Util/TextDataUtil/GuardedOperations.cs
@@ -372,7 +372,7 @@ namespace Microsoft.VisualStudio.Text.Utilities
{
try
{
- BeforeCallingEventHandler(call);
+ BeforeCallingExtensionPoint(errorSource ?? call);
call();
}
catch (Exception e)
@@ -381,7 +381,7 @@ namespace Microsoft.VisualStudio.Text.Utilities
}
finally
{
- AfterCallingEventHandler(call);
+ AfterCallingExtensionPoint(errorSource ?? call);
}
}
@@ -389,7 +389,7 @@ namespace Microsoft.VisualStudio.Text.Utilities
{
try
{
- BeforeCallingEventHandler(call);
+ BeforeCallingExtensionPoint(errorSource ?? call);
return call();
}
catch (Exception e)
@@ -400,8 +400,8 @@ namespace Microsoft.VisualStudio.Text.Utilities
}
finally
{
- AfterCallingEventHandler(call);
- }
+ AfterCallingExtensionPoint(errorSource ?? call);
+ }
}
public void CallExtensionPoint(Action call)
@@ -522,6 +522,26 @@ namespace Microsoft.VisualStudio.Text.Utilities
}
}
+ private void AfterCallingExtensionPoint(object extensionPoint)
+ {
+ if (PerfTrackers.Count == 0)
+ {
+ return;
+ }
+
+ foreach (var perfTracker in PerfTrackers)
+ {
+ try
+ {
+ perfTracker.AfterCallingExtension(extensionPoint);
+ }
+ catch (Exception e)
+ {
+ HandleException(perfTracker, e);
+ }
+ }
+ }
+
private void BeforeCallingEventHandler(Delegate handler)
{
if (PerfTrackers.Count == 0)
@@ -542,6 +562,26 @@ namespace Microsoft.VisualStudio.Text.Utilities
}
}
+ private void BeforeCallingExtensionPoint(object extensionPoint)
+ {
+ if (PerfTrackers.Count == 0)
+ {
+ return;
+ }
+
+ foreach (var perfTracker in PerfTrackers)
+ {
+ try
+ {
+ perfTracker.BeforeCallingExtension(extensionPoint);
+ }
+ catch (Exception e)
+ {
+ HandleException(perfTracker, e);
+ }
+ }
+ }
+
public void HandleException(object errorSource, Exception e)
{
bool handled = false;