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:
Diffstat (limited to 'src')
-rw-r--r--src/Language/Impl/Language/Completion/AsyncCompletionBroker.cs94
-rw-r--r--src/Language/Impl/Language/Completion/AsyncCompletionSession.cs91
-rw-r--r--src/Language/Impl/Language/Completion/CompletionCommandHandlers.cs86
-rw-r--r--src/Language/Impl/Language/Completion/DefaultCompletionService.cs14
-rw-r--r--src/Language/Impl/Language/Completion/SuggestionModeCompletionItemSource.cs9
-rw-r--r--src/Microsoft.VisualStudio.Text.Implementation.csproj6
6 files changed, 192 insertions, 108 deletions
diff --git a/src/Language/Impl/Language/Completion/AsyncCompletionBroker.cs b/src/Language/Impl/Language/Completion/AsyncCompletionBroker.cs
index 72bd1d2..6a91623 100644
--- a/src/Language/Impl/Language/Completion/AsyncCompletionBroker.cs
+++ b/src/Language/Impl/Language/Completion/AsyncCompletionBroker.cs
@@ -18,14 +18,10 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
internal class AsyncCompletionBroker : IAsyncCompletionBroker
{
[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; }
+ private ILoggingServiceInternal Logger;
[Import]
- internal IGuardedOperations GuardedOperations { get; set; }
+ private IGuardedOperations GuardedOperations;
[Import]
private JoinableTaskContext JoinableTaskContext;
@@ -33,6 +29,10 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
[Import]
private IContentTypeRegistryService ContentTypeRegistryService;
+ // Used exclusively for legacy telemetry
+ [Import(AllowDefault = true)]
+ private ITextDocumentFactoryService TextDocumentFactoryService;
+
[ImportMany]
private IEnumerable<Lazy<ICompletionPresenterProvider, IOrderableContentTypeMetadata>> UnorderedPresenterProviders;
@@ -43,15 +43,15 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
private IEnumerable<Lazy<IAsyncCompletionServiceProvider, IOrderableContentTypeMetadata>> UnorderedCompletionServiceProviders;
private IList<Lazy<ICompletionPresenterProvider, IOrderableContentTypeMetadata>> _orderedPresenterProviders;
- internal IList<Lazy<ICompletionPresenterProvider, IOrderableContentTypeMetadata>> OrderedPresenterProviders
+ private IList<Lazy<ICompletionPresenterProvider, IOrderableContentTypeMetadata>> OrderedPresenterProviders
=> _orderedPresenterProviders ?? (_orderedPresenterProviders = Orderer.Order(UnorderedPresenterProviders));
private IList<Lazy<IAsyncCompletionItemSourceProvider, IOrderableContentTypeMetadata>> _orderedCompletionItemSourceProviders;
- internal IList<Lazy<IAsyncCompletionItemSourceProvider, IOrderableContentTypeMetadata>> OrderedCompletionItemSourceProviders
+ private IList<Lazy<IAsyncCompletionItemSourceProvider, IOrderableContentTypeMetadata>> OrderedCompletionItemSourceProviders
=> _orderedCompletionItemSourceProviders ?? (_orderedCompletionItemSourceProviders = Orderer.Order(UnorderedCompletionItemSourceProviders));
private IList<Lazy<IAsyncCompletionServiceProvider, IOrderableContentTypeMetadata>> _orderedCompletionServiceProviders;
- internal IList<Lazy<IAsyncCompletionServiceProvider, IOrderableContentTypeMetadata>> OrderedCompletionServiceProviders
+ private IList<Lazy<IAsyncCompletionServiceProvider, IOrderableContentTypeMetadata>> OrderedCompletionServiceProviders
=> _orderedCompletionServiceProviders ?? (_orderedCompletionServiceProviders = Orderer.Order(UnorderedCompletionServiceProviders));
private ImmutableDictionary<IContentType, ImmutableSortedSet<char>> _commitCharacters = ImmutableDictionary<IContentType, ImmutableSortedSet<char>>.Empty;
@@ -60,12 +60,26 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
private ImmutableDictionary<IContentType, ICompletionPresenterProvider> _cachedPresenterProviders = ImmutableDictionary<IContentType, ICompletionPresenterProvider>.Empty;
private bool firstRun = true; // used only for diagnostics
private bool _firstInvocationReported; // used for "time to code"
-
private StableContentTypeComparer _contentTypeComparer;
+ private const string IsCompletionAvailableProperty = "IsCompletionAvailable";
- internal void DismissSession(IAsyncCompletionSession session)
+ private Dictionary<IContentType, bool> FeatureAvailabilityByContentType = new Dictionary<IContentType, bool>();
+
+ bool IAsyncCompletionBroker.IsCompletionSupported(IContentType contentType)
{
- session.TextView.Properties.RemoveProperty(typeof(IAsyncCompletionSession));
+ bool featureIsAvailable;
+ if (FeatureAvailabilityByContentType.TryGetValue(contentType, out featureIsAvailable))
+ {
+ return featureIsAvailable;
+ }
+
+ featureIsAvailable = UnorderedCompletionItemSourceProviders
+ .Any(n => n.Metadata.ContentTypes.Any(ct => contentType.IsOfType(ct)));
+ featureIsAvailable &= UnorderedCompletionServiceProviders
+ .Any(n => n.Metadata.ContentTypes.Any(ct => contentType.IsOfType(ct)));
+
+ FeatureAvailabilityByContentType[contentType] = featureIsAvailable;
+ return featureIsAvailable;
}
IAsyncCompletionSession IAsyncCompletionBroker.TriggerCompletion(ITextView textView, SnapshotPoint triggerLocation, char typedChar)
@@ -77,9 +91,9 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
}
var sourcesWithData = MetadataUtilities<IAsyncCompletionItemSourceProvider>.GetBuffersAndImports(textView, triggerLocation, GetCompletionItemSourceProviders);
+ var cachedData = new CompletionSourcesWithData(sourcesWithData);
foreach (var sourceWithData in sourcesWithData)
{
- // TODO: pass the sources to TriggerCompletion
var sourceProvider = GuardedOperations.InstantiateExtension(this, sourceWithData.import); // TODO: consider caching this
var source = sourceProvider.GetOrCreate(textView);
var candidateSpan = GuardedOperations.CallExtensionPoint(
@@ -92,22 +106,21 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
{
var mappingSpan = textView.BufferGraph.CreateMappingSpan(candidateSpan.Value, SpanTrackingMode.EdgeInclusive);
var applicableSpan = mappingSpan.GetSpans(textView.TextBuffer)[0];
- return TriggerCompletion(textView, triggerLocation, applicableSpan);
+ return TriggerCompletion(textView, triggerLocation, applicableSpan, cachedData);
}
}
return null;
}
- private IAsyncCompletionSession TriggerCompletion(ITextView textView, SnapshotPoint triggerLocation, SnapshotSpan applicableSpan)
- {// TODO: If we are in virtual space, create real space. Look at CompletionTrigger.cs
+ private IAsyncCompletionSession TriggerCompletion(ITextView textView, SnapshotPoint triggerLocation, SnapshotSpan applicableSpan, CompletionSourcesWithData sources)
+ {
var session = GetSession(textView);
if (session != null)
{
return session;
}
- var sourcesWithData = MetadataUtilities<IAsyncCompletionItemSourceProvider>.GetBuffersAndImports(textView, triggerLocation, GetCompletionItemSourceProviders);
- if (!sourcesWithData.Any())
+ if (!sources.Data.Any())
{
// There is no completion source available for this buffer
return null;
@@ -116,9 +129,8 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
//var sourcesWithLocations = new Dictionary<IAsyncCompletionItemSource, SnapshotPoint>();
var potentialCommitCharsBuilder = ImmutableArray.CreateBuilder<char>();
var sourcesWithLocations = new Dictionary<IAsyncCompletionItemSource, SnapshotPoint>();
- foreach (var sourceWithData in sourcesWithData)
+ foreach (var sourceWithData in sources.Data)
{
- // Unfortunately we can't use for loop here because IDictionary.Keys is ICollection which can't be accessed with an indexer
var sourceProvider = GuardedOperations.InstantiateExtension(this, sourceWithData.import); // TODO: consider caching this
GuardedOperations.CallExtensionPoint(
errorSource: sourceProvider,
@@ -154,7 +166,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
}
var telemetry = GetOrCreateTelemetry(textView);
- session = new AsyncCompletionSession(applicableSpan, potentialCommitCharsBuilder.ToImmutable(), JoinableTaskContext.Factory, presenterProvider, sourcesWithLocations, service, this, textView, telemetry);
+ session = new AsyncCompletionSession(applicableSpan, potentialCommitCharsBuilder.ToImmutable(), JoinableTaskContext.Factory, presenterProvider, sourcesWithLocations, service, this, textView, telemetry, GuardedOperations);
textView.Properties.AddProperty(typeof(IAsyncCompletionSession), session);
textView.Closed += TextView_Closed;
@@ -285,6 +297,29 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
return null;
}
+ /// <summary>
+ /// This method is used by <see cref="IAsyncCompletionSession"/> to inform the broker that it should forget about the session. Used when dismissing.
+ /// This method does not dismiss the session!
+ /// </summary>
+ /// <param name="session">Session being dismissed</param>
+ internal void ForgetSession(IAsyncCompletionSession session)
+ {
+ session.TextView.Properties.RemoveProperty(typeof(IAsyncCompletionSession));
+ }
+
+ /// <summary>
+ /// Wrapper around complex parameters. This is a candidate for refactoring.
+ /// </summary>
+ private struct CompletionSourcesWithData
+ {
+ internal IEnumerable<(ITextBuffer buffer, SnapshotPoint point, Lazy<IAsyncCompletionItemSourceProvider, IOrderableContentTypeMetadata> import)> Data;
+
+ public CompletionSourcesWithData(IEnumerable<(ITextBuffer buffer, SnapshotPoint point, Lazy<IAsyncCompletionItemSourceProvider, IOrderableContentTypeMetadata> import)> data)
+ {
+ Data = data;
+ }
+ }
+
// Helper methods for telemetry:
private CompletionTelemetryHost GetOrCreateTelemetry(ITextView textView)
{
@@ -341,22 +376,5 @@ 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 textView)
- {
- bool featureIsAvailable;
- if (textView.Properties.TryGetProperty(IsCompletionAvailableProperty, out featureIsAvailable))
- {
- return featureIsAvailable;
- }
-
- featureIsAvailable = UnorderedCompletionItemSourceProviders
- .Any(n => n.Metadata.ContentTypes.Any(ct => textView.TextBuffer.ContentType.IsOfType(ct)));
- featureIsAvailable &= UnorderedCompletionServiceProviders
- .Any(n => n.Metadata.ContentTypes.Any(ct => textView.TextBuffer.ContentType.IsOfType(ct)));
- textView.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 7933064..0469423 100644
--- a/src/Language/Impl/Language/Completion/AsyncCompletionSession.cs
+++ b/src/Language/Impl/Language/Completion/AsyncCompletionSession.cs
@@ -28,7 +28,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
private readonly JoinableTaskFactory _jtf;
private readonly ICompletionPresenterProvider _presenterProvider;
private readonly AsyncCompletionBroker _broker;
- private readonly ITextView _view;
+ private readonly ITextView _textView;
private readonly IGuardedOperations _guardedOperations;
private readonly ImmutableArray<char> _potentialCommitChars;
@@ -62,18 +62,19 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
// Facilitate experience when there are no items to display
private bool _selectionModeBeforeNoResultFallback;
private bool _inNoResultFallback;
+ private bool _ignoreCaretMovement;
public event EventHandler<CompletionItemEventArgs> ItemCommitted;
public event EventHandler Dismissed;
public event EventHandler<CompletionItemsWithHighlightEventArgs> ItemsUpdated;
- public ITextView TextView => _view;
+ public ITextView TextView => _textView;
public bool IsDismissed => _isDismissed;
public AsyncCompletionSession(SnapshotSpan applicableSpan, ImmutableArray<char> potentialCommitChars, JoinableTaskFactory jtf,
ICompletionPresenterProvider presenterProvider, IDictionary<IAsyncCompletionItemSource, SnapshotPoint> completionSources,
- IAsyncCompletionService completionService, AsyncCompletionBroker broker, ITextView view, CompletionTelemetryHost telemetryHost)
+ IAsyncCompletionService completionService, AsyncCompletionBroker broker, ITextView view, CompletionTelemetryHost telemetryHost, IGuardedOperations guardedOperations)
{
_initialApplicableSpan = applicableSpan;
_potentialCommitChars = potentialCommitChars;
@@ -82,11 +83,11 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
_broker = broker;
_completionSources = completionSources;
_completionService = completionService;
- _view = view;
- _guardedOperations = broker.GuardedOperations;
+ _textView = view;
+ _guardedOperations = guardedOperations;
_telemetry = new CompletionSessionTelemetry(telemetryHost, completionService, presenterProvider);
PageStepSize = presenterProvider?.ResultsPerPage ?? 1;
- _view.Caret.PositionChanged += OnCaretPositionChanged;
+ _textView.Caret.PositionChanged += OnCaretPositionChanged;
}
bool IAsyncCompletionSession.CommitIfUnique(CancellationToken token)
@@ -120,10 +121,10 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
/// <param name="token">Command handler infrastructure provides a token that we should pass to the language service's custom commit method.</param>
/// <param name="typedChar">It is default(char) when commit was requested by an explcit command (e.g. hitting Tab, Enter or clicking)
/// and it is not default(char) when commit happens as a result of typing a commit character.</param>
- CustomCommitBehavior IAsyncCompletionSession.Commit(CancellationToken token, char typedChar)
+ CommitBehavior IAsyncCompletionSession.Commit(CancellationToken token, char typedChar)
{
if (_isDismissed)
- return CustomCommitBehavior.None;
+ return CommitBehavior.None;
var lastModel = _computation.WaitAndGetResult();
@@ -131,12 +132,12 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
{
// In soft selection mode, user commits explicitly (click, tab, e.g. not tied to a text change). Otherwise, we dismiss the session
((IAsyncCompletionSession)this).Dismiss();
- return CustomCommitBehavior.None;
+ return CommitBehavior.None;
}
else if (lastModel.SelectSuggestionMode && string.IsNullOrWhiteSpace(lastModel.SuggestionModeItem?.InsertText))
{
// In suggestion mode, don't commit empty suggestion (suggestion item temporarily shows description of suggestion mode)
- return CustomCommitBehavior.None;
+ return CommitBehavior.None;
}
else if (lastModel.SelectSuggestionMode)
{
@@ -147,7 +148,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
{
// There is nothing to commit
Dismiss();
- return CustomCommitBehavior.None;
+ return CommitBehavior.None;
}
else
{
@@ -156,9 +157,9 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
}
}
- private CustomCommitBehavior Commit(char typedChar, CompletionItem itemToCommit, CancellationToken token)
+ private CommitBehavior Commit(char typedChar, CompletionItem itemToCommit, CancellationToken token)
{
- CustomCommitBehavior result = CustomCommitBehavior.None;
+ CommitBehavior result = CommitBehavior.None;
if (_isDismissed)
return result;
@@ -168,18 +169,24 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
throw new InvalidOperationException($"{nameof(IAsyncCompletionSession)}.{nameof(IAsyncCompletionSession.Commit)} must be callled from UI thread.");
UiStopwatch.Restart();
+
+ // Pass appropriate buffer to the item's provider
+ var buffer = _completionSources[itemToCommit.Source].Snapshot.TextBuffer;
if (itemToCommit.UseCustomCommit)
{
- // Pass appropriate buffer to the item's provider
- var buffer = _completionSources[itemToCommit.Source].Snapshot.TextBuffer;
result = _guardedOperations.CallExtensionPoint(
errorSource: itemToCommit.Source,
- call: () => itemToCommit.Source.CustomCommit(_view, buffer, itemToCommit, lastModel.ApplicableSpan, typedChar, token),
- valueOnThrow: CustomCommitBehavior.None);
+ call: () => itemToCommit.Source.CustomCommit(_textView, buffer, itemToCommit, lastModel.ApplicableSpan, typedChar, token),
+ valueOnThrow: CommitBehavior.None);
}
else
{
- InsertIntoBuffer(_view, lastModel, itemToCommit.InsertText, typedChar);
+ result = _guardedOperations.CallExtensionPoint(
+ errorSource: itemToCommit.Source,
+ call: () => itemToCommit.Source.GetDefaultCommitBehavior(_textView, buffer, itemToCommit, lastModel.ApplicableSpan, typedChar, token),
+ valueOnThrow: CommitBehavior.None);
+
+ InsertIntoBuffer(_textView, lastModel, itemToCommit.InsertText, typedChar);
}
UiStopwatch.Stop();
_telemetry.RecordCommitted(UiStopwatch.ElapsedMilliseconds, itemToCommit);
@@ -205,10 +212,9 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
return;
_isDismissed = true;
- _broker.DismissSession(this);
+ _broker.ForgetSession(this);
_guardedOperations.RaiseEvent(this, Dismissed);
- _view.Caret.PositionChanged -= OnCaretPositionChanged;
- _computation = null;
+ _textView.Caret.PositionChanged -= OnCaretPositionChanged;
_computationCancellation.Cancel();
if (_gui != null)
@@ -229,7 +235,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
}
}
- void IAsyncCompletionSession.OpenOrUpdate(ITextView view, CompletionTrigger trigger, SnapshotPoint triggerLocation)
+ void IAsyncCompletionSession.OpenOrUpdate(CompletionTrigger trigger, SnapshotPoint triggerLocation)
{
if (_isDismissed)
return;
@@ -237,14 +243,14 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
if (_computation == null)
{
_computation = new ModelComputation<CompletionModel>(PrioritizedTaskScheduler.AboveNormalInstance, _computationCancellation.Token, _guardedOperations, this);
- _computation.Enqueue((model, token) => GetInitialModel(view, trigger, triggerLocation, token), updateUi: false);
+ _computation.Enqueue((model, token) => GetInitialModel(_textView, trigger, triggerLocation, token), updateUi: false);
}
var taskId = Interlocked.Increment(ref _lastFilteringTaskId);
_computation.Enqueue((model, token) => UpdateSnapshot(model, trigger, FromCompletionTriggerReason(trigger.Reason), triggerLocation, token, taskId), updateUi: true);
}
- internal void InvokeAndCommitIfUnique(ITextView view, CompletionTrigger trigger, SnapshotPoint triggerLocation, CancellationToken token)
+ internal void InvokeAndCommitIfUnique(CompletionTrigger trigger, SnapshotPoint triggerLocation, CancellationToken token)
{
if (_isDismissed)
return;
@@ -252,7 +258,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
if (_computation == null)
{
// Do not recompute, since this may change the selection.
- ((IAsyncCompletionSession)this).OpenOrUpdate(view, trigger, triggerLocation);
+ ((IAsyncCompletionSession)this).OpenOrUpdate(trigger, triggerLocation);
}
if (((IAsyncCompletionSession)this).CommitIfUnique(token))
@@ -336,14 +342,14 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
/// and return whether any source would like to commit completion.
/// </summary>
/// <remarks>This method must run on UI thread because of mapping the point across buffers.</remarks>
- bool IAsyncCompletionSession.ShouldCommit(ITextView view, char typeChar, SnapshotPoint triggerLocation)
+ bool IAsyncCompletionSession.ShouldCommit(char typeChar, SnapshotPoint triggerLocation)
{
if (!_jtf.Context.IsOnMainThread)
throw new InvalidOperationException($"This method must be callled on the UI thread.");
if (_potentialCommitChars.Contains(typeChar))
{
- var mappingPoint = view.BufferGraph.CreateMappingPoint(triggerLocation, PointTrackingMode.Negative);
+ var mappingPoint = _textView.BufferGraph.CreateMappingPoint(triggerLocation, PointTrackingMode.Negative);
return _completionSources
.Select(p => (p, mappingPoint.GetPoint(p.Value.Snapshot.TextBuffer, PositionAffinity.Predecessor)))
.Where(n => n.Item2.HasValue)
@@ -356,17 +362,30 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
}
/// <summary>
- /// Monitors when user scrolled outside of the completion area.
- /// Note that this event is not raised during regular typing
- /// Also note that typing stretches the completion area
+ /// Monitors when user scrolled outside of the applicable span. Note that:
+ /// * This event is not raised during regular typing.
+ /// * This event is raised by brace completion.
+ /// * Typing stretches the applicable span
/// </summary>
private void OnCaretPositionChanged(object sender, CaretPositionChangedEventArgs e)
{
// 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
+ if (_ignoreCaretMovement)
+ return;
+
_computation.Enqueue((model, token) => HandleCaretPositionChanged(model, e.NewPosition), updateUi: true);
}
+ internal void IgnoreCaretMovement(bool ignore)
+ {
+ _ignoreCaretMovement = ignore;
+ if (!ignore)
+ {
+ // Don't let the session exist in invalid state: ensure that the location of the session is still valid
+ _computation?.Enqueue((model, token) => HandleCaretPositionChanged(model, _textView.Caret.Position), updateUi: true);
+ }
+ }
+
async Task ICompletionComputationCallbackHandler<CompletionModel>.UpdateUi(CompletionModel model)
{
if (_presenterProvider == null) return;
@@ -388,14 +407,14 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
UiStopwatch.Restart();
if (_gui == null)
{
- _gui = _guardedOperations.CallExtensionPoint(errorSource: _presenterProvider, call: () => _presenterProvider.GetOrCreate(_view), valueOnThrow: null);
+ _gui = _guardedOperations.CallExtensionPoint(errorSource: _presenterProvider, call: () => _presenterProvider.GetOrCreate(_textView), valueOnThrow: null);
if (_gui != null)
{
_guardedOperations.CallExtensionPoint(
errorSource: _gui,
call: () =>
{
- _gui = _presenterProvider.GetOrCreate(_view);
+ _gui = _presenterProvider.GetOrCreate(_textView);
_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;
@@ -461,7 +480,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
ComputationStopwatch.Restart();
var sortedList = await _guardedOperations.CallExtensionPointAsync(
errorSource: _completionService,
- asyncCall: () => _completionService.SortCompletionListAsync(initialCompletionItems, trigger.Reason, triggerLocation.Snapshot, applicableSpan, _view, token),
+ asyncCall: () => _completionService.SortCompletionListAsync(initialCompletionItems, trigger.Reason, triggerLocation.Snapshot, applicableSpan, _textView, token),
valueOnThrow: initialCompletionItems);
ComputationStopwatch.Stop();
_telemetry.RecordProcessing(ComputationStopwatch.ElapsedMilliseconds, initialCompletionItems.Length);
@@ -536,7 +555,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
triggerLocation.Snapshot,
model.ApplicableSpan,
model.Filters,
- _view,
+ _textView,
token),
valueOnThrow: null);
@@ -620,7 +639,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
model.Snapshot,
model.ApplicableSpan,
newFilters,
- _view,
+ _textView,
token),
valueOnThrow: null);
diff --git a/src/Language/Impl/Language/Completion/CompletionCommandHandlers.cs b/src/Language/Impl/Language/Completion/CompletionCommandHandlers.cs
index ed7aecd..45694ad 100644
--- a/src/Language/Impl/Language/Completion/CompletionCommandHandlers.cs
+++ b/src/Language/Impl/Language/Completion/CompletionCommandHandlers.cs
@@ -56,10 +56,10 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
/// </summary>
/// <param name="view"></param>
/// <returns></returns>
- private CommandState Available(ITextView view)
+ private CommandState Available(IContentType contentType)
{
return ModernCompletionFeature.GetFeatureState(ExperimentationService)
- && Broker.IsCompletionSupported(view)
+ && Broker.IsCompletionSupported(contentType)
? CommandState.Available
: CommandState.Unspecified;
}
@@ -95,7 +95,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
{
var trigger = new CompletionTrigger(CompletionTriggerReason.Deletion);
var location = args.TextView.Caret.Position.BufferPosition;
- session.OpenOrUpdate(args.TextView, trigger, location);
+ session.OpenOrUpdate(trigger, location);
}
}
@@ -114,40 +114,56 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
}
CommandState ICommandHandler<InvokeCompletionListCommandArgs>.GetCommandState(InvokeCompletionListCommandArgs args)
- => Available(args.TextView);
+ => Available(args.SubjectBuffer.ContentType);
bool ICommandHandler<InvokeCompletionListCommandArgs>.ExecuteCommand(InvokeCompletionListCommandArgs args, CommandExecutionContext executionContext)
{
+ // If the caret is buried in virtual space, we should realize this virtual space before triggering the session.
+ if (args.TextView.Caret.InVirtualSpace)
+ {
+ IEditorOperations editorOperations = EditorOperationsFactoryService.GetEditorOperations(args.TextView);
+ // We can realize virtual space by inserting nothing through the editor operations.
+ editorOperations?.InsertText("");
+ }
+
var trigger = new CompletionTrigger(CompletionTriggerReason.Invoke);
var location = args.TextView.Caret.Position.BufferPosition;
var session = Broker.TriggerCompletion(args.TextView, location, default(char));
if (session != null)
{
- session.OpenOrUpdate(args.TextView, trigger, location);
+ session.OpenOrUpdate(trigger, location);
return true;
}
return false;
}
CommandState ICommandHandler<CommitUniqueCompletionListItemCommandArgs>.GetCommandState(CommitUniqueCompletionListItemCommandArgs args)
- => Available(args.TextView);
+ => Available(args.SubjectBuffer.ContentType);
bool ICommandHandler<CommitUniqueCompletionListItemCommandArgs>.ExecuteCommand(CommitUniqueCompletionListItemCommandArgs args, CommandExecutionContext executionContext)
{
+ // If the caret is buried in virtual space, we should realize this virtual space before triggering the session.
+ if (args.TextView.Caret.InVirtualSpace)
+ {
+ IEditorOperations editorOperations = EditorOperationsFactoryService.GetEditorOperations(args.TextView);
+ // We can realize virtual space by inserting nothing through the editor operations.
+ editorOperations?.InsertText("");
+ }
+
var trigger = new CompletionTrigger(CompletionTriggerReason.InvokeAndCommitIfUnique);
var location = args.TextView.Caret.Position.BufferPosition;
var session = Broker.TriggerCompletion(args.TextView, location, default(char));
if (session != null)
{
var sessionInternal = session as AsyncCompletionSession;
- sessionInternal?.InvokeAndCommitIfUnique(args.TextView, trigger, location, executionContext.OperationContext.UserCancellationToken);
+ sessionInternal?.InvokeAndCommitIfUnique(trigger, location, executionContext.OperationContext.UserCancellationToken);
return true;
}
return false;
}
CommandState ICommandHandler<InsertSnippetCommandArgs>.GetCommandState(InsertSnippetCommandArgs args)
- => Available(args.TextView);
+ => Available(args.SubjectBuffer.ContentType);
bool ICommandHandler<InsertSnippetCommandArgs>.ExecuteCommand(InsertSnippetCommandArgs args, CommandExecutionContext executionContext)
{
@@ -156,7 +172,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
}
CommandState ICommandHandler<ToggleCompletionModeCommandArgs>.GetCommandState(ToggleCompletionModeCommandArgs args)
- => Available(args.TextView);
+ => Available(args.SubjectBuffer.ContentType);
bool ICommandHandler<ToggleCompletionModeCommandArgs>.ExecuteCommand(ToggleCompletionModeCommandArgs args, CommandExecutionContext executionContext)
{
@@ -186,7 +202,7 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
{
var trigger = new CompletionTrigger(CompletionTriggerReason.Deletion);
var location = args.TextView.Caret.Position.BufferPosition;
- session.OpenOrUpdate(args.TextView, trigger, location);
+ session.OpenOrUpdate(trigger, location);
}
}
@@ -263,35 +279,50 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
}
CommandState IChainedCommandHandler<TypeCharCommandArgs>.GetCommandState(TypeCharCommandArgs args, Func<CommandState> nextCommandHandler)
- => Available(args.TextView);
+ => Available(args.SubjectBuffer.ContentType);
void IChainedCommandHandler<TypeCharCommandArgs>.ExecuteCommand(TypeCharCommandArgs args, Action nextCommandHandler, CommandExecutionContext executionContext)
{
var initialTextSnapshot = args.TextView.TextSnapshot;
var view = args.TextView;
+ var location = view.Caret.Position.BufferPosition;
+
+ // Note regarding undo: When completion and brace completion happen together, completion should be first on the undo stack.
+ // Effectively, we want to first undo the completion, leaving brace completion intact. Second undo should undo brace completion.
+ // To achieve this, we create a transaction in which we commit and reapply brace completion (via nextCommandHandler).
+ // Please read "Note regarding undo" comments in this method that explain the implementation choices.
+ // Hopefully an upcoming upgrade of the undo mechanism will allow us to undo out of order and vastly simplify this method.
+
+ // Note regarding undo: In a corner case of typing closing brace over existing closing brace,
+ // Roslyn brace completion does not perform an edit. It moves the caret outside of session's applicable span,
+ // which dismisses the session. Put the session in a state where it will not dismiss when caret leaves the applicable span.
+ /*
+ // Unfortunately, preserving this session means that ultimately replaying the key will insert second brace instead of overtyping.
+ // Actually, even obtaining session before nextCommandHandler messes this up. I don't understand this yet and for now I will keep this code commented out. Completing over closing brace is now a known bug.
+ if (sessionToCommit != null && args.TextView.TextBuffer == args.SubjectBuffer)
+ {
+ ((AsyncCompletionSession)sessionToCommit).IgnoreCaretMovement(ignore: true);
+ }
+ */
+ // Execute other commands in the chain to see the change in the buffer. This includes brace completion.
+ // Note regarding undo: This will be undone second
+ nextCommandHandler();
if (args.TextView.TextBuffer != args.SubjectBuffer)
{
// We are only inteterested in the top buffer. Currently, commanding implementation calls us multiple times, once per each buffer.
- // Allow other command handlers to act.
- nextCommandHandler();
return;
}
- var location = view.Caret.Position.BufferPosition;
- // Call ShouldCommit before nextCommandHandler so that extenders
+ // Pass location from before calling nextCommandHandler so that extenders
// get the same view of the buffer in both ShouldCommit and Commit
var sessionToCommit = Broker.GetSession(args.TextView);
- var shouldCommit = sessionToCommit?.ShouldCommit(view, args.TypedChar, location);
-
- if (shouldCommit == true)
+ if (sessionToCommit?.ShouldCommit(args.TypedChar, location) == true)
{
- // Execute other commands in the chain to see the change in the buffer.
- nextCommandHandler();
-
// Buffer has changed, update the snapshot
location = view.Caret.Position.BufferPosition;
+ // Note regarding undo: this transaction will be undone first
using (var undoTransaction = new CaretPreservingEditTransaction("Completion", view, UndoHistoryRegistry, EditorOperationsFactoryService))
{
UndoUtilities.RollbackToBeforeTypeChar(initialTextSnapshot, args.SubjectBuffer);
@@ -299,18 +330,19 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
var customBehavior = sessionToCommit.Commit(executionContext.OperationContext.UserCancellationToken, args.TypedChar);
- if ((customBehavior & CustomCommitBehavior.SuppressFurtherCommandHandlers) == 0)
+ if ((customBehavior & CommitBehavior.SuppressFurtherCommandHandlers) == 0)
nextCommandHandler(); // Replay the key, so that we get brace completion.
// Complete the transaction before stopping it.
undoTransaction.Complete();
}
}
- else
+ /*
+ if (sessionToCommit != null)
{
- nextCommandHandler();
+ ((AsyncCompletionSession)sessionToCommit).IgnoreCaretMovement(ignore: false);
}
-
+ */
// Buffer might have changed. Update it for when we try to trigger new session.
location = view.Caret.Position.BufferPosition;
@@ -318,14 +350,14 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
var session = Broker.GetSession(args.TextView);
if (session != null)
{
- session.OpenOrUpdate(view, trigger, location);
+ session.OpenOrUpdate(trigger, location);
}
else
{
var newSession = Broker.TriggerCompletion(args.TextView, location, args.TypedChar);
if (newSession != null)
{
- newSession?.OpenOrUpdate(view, trigger, location);
+ newSession?.OpenOrUpdate(trigger, location);
}
}
}
diff --git a/src/Language/Impl/Language/Completion/DefaultCompletionService.cs b/src/Language/Impl/Language/Completion/DefaultCompletionService.cs
index 50f3728..5b7066f 100644
--- a/src/Language/Impl/Language/Completion/DefaultCompletionService.cs
+++ b/src/Language/Impl/Language/Completion/DefaultCompletionService.cs
@@ -161,6 +161,11 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
return CustomCommitBehavior.None;
}
+ CommitBehavior IAsyncCompletionItemSource.GetDefaultCommitBehavior(ITextView view, ITextBuffer buffer, CompletionItem item, ITrackingSpan applicableSpan, char typeChar, CancellationToken token)
+ {
+ return CommitBehavior.None;
+ }
+
async Task<CompletionContext> IAsyncCompletionItemSource.GetCompletionContextAsync(CompletionTrigger trigger, SnapshotPoint triggerLocation, SnapshotSpan triggerLocation, CancellationToken token)
{
return await Task.FromResult(new CompletionContext(
@@ -229,9 +234,14 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
{
private static readonly ImmutableArray<char> commitCharacters = ImmutableArray.Create(' ', '>', '=');
- CustomCommitBehavior IAsyncCompletionItemSource.CustomCommit(Text.Editor.ITextView view, ITextBuffer buffer, CompletionItem item, ITrackingSpan applicableSpan, char typeChar, CancellationToken token)
+ CommitBehavior IAsyncCompletionItemSource.CustomCommit(Text.Editor.ITextView view, ITextBuffer buffer, CompletionItem item, ITrackingSpan applicableSpan, char typeChar, CancellationToken token)
{
- return CustomCommitBehavior.None;
+ return CommitBehavior.None;
+ }
+
+ CommitBehavior IAsyncCompletionItemSource.GetDefaultCommitBehavior(ITextView view, ITextBuffer buffer, CompletionItem item, ITrackingSpan applicableSpan, char typeChar, CancellationToken token)
+ {
+ return CommitBehavior.None;
}
async Task<CompletionContext> IAsyncCompletionItemSource.GetCompletionContextAsync(CompletionTrigger trigger, SnapshotPoint triggerLocation, SnapshotSpan applicableSpan, CancellationToken token)
diff --git a/src/Language/Impl/Language/Completion/SuggestionModeCompletionItemSource.cs b/src/Language/Impl/Language/Completion/SuggestionModeCompletionItemSource.cs
index a5b34e4..c86589c 100644
--- a/src/Language/Impl/Language/Completion/SuggestionModeCompletionItemSource.cs
+++ b/src/Language/Impl/Language/Completion/SuggestionModeCompletionItemSource.cs
@@ -23,9 +23,14 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
}
}
- CustomCommitBehavior IAsyncCompletionItemSource.CustomCommit(ITextView view, ITextBuffer buffer, CompletionItem item, ITrackingSpan applicableSpan, char typeChar, CancellationToken token)
+ CommitBehavior IAsyncCompletionItemSource.CustomCommit(ITextView view, ITextBuffer buffer, CompletionItem item, ITrackingSpan applicableSpan, char typeChar, CancellationToken token)
{
- return CustomCommitBehavior.None;
+ return CommitBehavior.None;
+ }
+
+ CommitBehavior IAsyncCompletionItemSource.GetDefaultCommitBehavior(ITextView view, ITextBuffer buffer, CompletionItem item, ITrackingSpan applicableSpan, char typeChar, CancellationToken token)
+ {
+ return CommitBehavior.None;
}
Task<CompletionContext> IAsyncCompletionItemSource.GetCompletionContextAsync(CompletionTrigger trigger, SnapshotPoint triggerLocation, SnapshotSpan applicableSpan, CancellationToken token)
diff --git a/src/Microsoft.VisualStudio.Text.Implementation.csproj b/src/Microsoft.VisualStudio.Text.Implementation.csproj
index 2ef1691..27cd98c 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.20-pre</Version>
+ <Version>15.0.21-pre</Version>
<AssemblyVersion>15.0.0.0</AssemblyVersion>
- <NuGetVersionEditor>15.7.124-preview-ge536746498</NuGetVersionEditor>
- <NuGetVersionLanguage>15.7.124-preview-ge536746498</NuGetVersionLanguage>
+ <NuGetVersionEditor>15.7.140-preview-gc7d2848231</NuGetVersionEditor>
+ <NuGetVersionLanguage>15.7.140-preview-gc7d2848231</NuGetVersionLanguage>
</PropertyGroup>
<PropertyGroup>