示例#1
0
        /// <summary>
        /// Attempts to convert VS Completion trigger into Roslyn completion trigger
        /// </summary>
        /// <param name="trigger">VS completion trigger</param>
        /// <param name="triggerLocation">Character.
        /// VS provides Backspace and Delete characters inside the trigger while Roslyn needs the char deleted by the trigger.
        /// Therefore, we provide this character separately and use it for Delete and Backspace cases only.
        /// We retrieve this character from triggerLocation.
        /// </param>
        /// <returns>Roslyn completion trigger</returns>
        internal static RoslynTrigger GetRoslynTrigger(EditorAsyncCompletionData.CompletionTrigger trigger, SnapshotPoint triggerLocation)
        {
            var completionTriggerKind = GetRoslynTriggerKind(trigger);

            if (completionTriggerKind == CompletionTriggerKind.Deletion)
            {
                var  snapshotBeforeEdit = trigger.ViewSnapshotBeforeTrigger;
                char characterRemoved;
                if (triggerLocation.Position >= 0 && triggerLocation.Position < snapshotBeforeEdit.Length)
                {
                    // If multiple characters were removed (selection), this finds the first character from the left.
                    characterRemoved = snapshotBeforeEdit[triggerLocation.Position];
                }
                else
                {
                    characterRemoved = (char)0;
                }

                return(RoslynTrigger.CreateDeletionTrigger(characterRemoved));
            }
            else
            {
                return(new RoslynTrigger(completionTriggerKind, trigger.Character));
            }
        }
示例#2
0
        /// <summary>
        /// Attempts to convert VS Completion trigger into Roslyn completion trigger
        /// </summary>
        /// <param name="trigger">VS completion trigger</param>
        /// <param name="triggerLocation">Character.
        /// VS provides Backspace and Delete characters inside the trigger while Roslyn needs the char deleted by the trigger.
        /// Therefore, we provide this character separately and use it for Delete and Backspace cases only.
        /// We retrieve this character from triggerLocation.
        /// </param>
        /// <returns>Roslyn completion trigger</returns>
        internal static RoslynTrigger GetRoslynTrigger(AsyncCompletionData.CompletionTrigger trigger, SnapshotPoint triggerLocation)
        {
            switch (trigger.Reason)
            {
            case AsyncCompletionData.CompletionTriggerReason.InvokeAndCommitIfUnique:
                return(new RoslynTrigger(CompletionTriggerKind.InvokeAndCommitIfUnique));

            case AsyncCompletionData.CompletionTriggerReason.Insertion:
                return(RoslynTrigger.CreateInsertionTrigger(trigger.Character));

            case AsyncCompletionData.CompletionTriggerReason.Deletion:
            case AsyncCompletionData.CompletionTriggerReason.Backspace:
                var  snapshotBeforeEdit = trigger.ViewSnapshotBeforeTrigger;
                char characterRemoved;
                if (triggerLocation.Position >= 0 && triggerLocation.Position < snapshotBeforeEdit.Length)
                {
                    // If multiple characters were removed (selection), this finds the first character from the left.
                    characterRemoved = snapshotBeforeEdit[triggerLocation.Position];
                }
                else
                {
                    characterRemoved = (char)0;
                }

                return(RoslynTrigger.CreateDeletionTrigger(characterRemoved));

            case AsyncCompletionData.CompletionTriggerReason.SnippetsMode:
                return(new RoslynTrigger(CompletionTriggerKind.Snippets));

            default:
                return(RoslynTrigger.Invoke);
            }
        }
示例#3
0
        private bool ShouldTriggerCompletion(
            AsyncCompletionData.CompletionTrigger trigger,
            SnapshotPoint triggerLocation,
            SourceText sourceText,
            Document document,
            CompletionService completionService)
        {
            // The trigger reason guarantees that user wants a completion.
            if (trigger.Reason == AsyncCompletionData.CompletionTriggerReason.Invoke ||
                trigger.Reason == AsyncCompletionData.CompletionTriggerReason.InvokeAndCommitIfUnique)
            {
                return(true);
            }

            //The user may be trying to invoke snippets through question-tab.
            // We may provide a completion after that.
            // Otherwise, tab should not be a completion trigger.
            if (trigger.Reason == AsyncCompletionData.CompletionTriggerReason.Insertion && trigger.Character == '\t')
            {
                return(TryInvokeSnippetCompletion(completionService, document, sourceText, triggerLocation.Position));
            }

            var roslynTrigger = Helpers.GetRoslynTrigger(trigger, triggerLocation);

            // The completion service decides that user may want a completion.
            if (completionService.ShouldTriggerCompletion(sourceText, triggerLocation.Position, roslynTrigger))
            {
                return(true);
            }

            return(false);
        }
示例#4
0
        public AsyncCompletionData.CompletionStartData InitializeCompletion(
            AsyncCompletionData.CompletionTrigger trigger,
            SnapshotPoint triggerLocation,
            CancellationToken cancellationToken)
        {
            // We take sourceText from document to get a snapshot span.
            // We would like to be sure that nobody changes buffers at the same time.
            AssertIsForeground();

            if (_textView.Selection.Mode == TextSelectionMode.Box)
            {
                // No completion with multiple selection
                return(AsyncCompletionData.CompletionStartData.DoesNotParticipateInCompletion);
            }

            var document = triggerLocation.Snapshot.GetOpenDocumentInCurrentContextWithChanges();

            if (document == null)
            {
                return(AsyncCompletionData.CompletionStartData.DoesNotParticipateInCompletion);
            }

            var service = document.GetLanguageService <CompletionService>();

            if (service == null)
            {
                return(AsyncCompletionData.CompletionStartData.DoesNotParticipateInCompletion);
            }

            // The Editor supports the option per textView.
            // There could be mixed desired behavior per textView and even per same completion session.
            // The right fix would be to send this information as a result of the method.
            // Then, the Editor would choose the right behavior for mixed cases.
            _textView.Options.GlobalOptions.SetOptionValue(NonBlockingCompletionEditorOption, !document.Project.Solution.Workspace.Options.GetOption(CompletionOptions.BlockForCompletionItems2, service.Language));

            // In case of calls with multiple completion services for the same view (e.g. TypeScript and C#), those completion services must not be called simultaneously for the same session.
            // Therefore, in each completion session we use a list of commit character for a specific completion service and a specific content type.
            _textView.Properties[PotentialCommitCharacters] = service.GetRules().DefaultCommitCharacters;

            // Reset a flag which means a snippet triggered by ? + Tab.
            // Set it later if met the condition.
            _snippetCompletionTriggeredIndirectly = false;

            CheckForExperimentStatus(_textView, document);

            var sourceText = document.GetTextSynchronously(cancellationToken);

            return(ShouldTriggerCompletion(trigger, triggerLocation, sourceText, document, service)
                ? new AsyncCompletionData.CompletionStartData(
                       participation: AsyncCompletionData.CompletionParticipation.ProvidesItems,
                       applicableToSpan: new SnapshotSpan(
                           triggerLocation.Snapshot,
                           service.GetDefaultCompletionListSpan(sourceText, triggerLocation.Position).ToSpan()))
                : AsyncCompletionData.CompletionStartData.DoesNotParticipateInCompletion);
示例#5
0
        public AsyncCompletionData.CompletionStartData InitializeCompletion(
            AsyncCompletionData.CompletionTrigger trigger,
            SnapshotPoint triggerLocation,
            CancellationToken cancellationToken)
        {
            // We take sourceText from document to get a snapshot span.
            // We would like to be sure that nobody changes buffers at the same time.
            AssertIsForeground();

            if (_textView.Selection.Mode == TextSelectionMode.Box)
            {
                // No completion with multiple selection
                return(AsyncCompletionData.CompletionStartData.DoesNotParticipateInCompletion);
            }

            var document = triggerLocation.Snapshot.GetOpenDocumentInCurrentContextWithChanges();

            if (document == null)
            {
                return(AsyncCompletionData.CompletionStartData.DoesNotParticipateInCompletion);
            }

            var service = document.GetLanguageService <CompletionService>();

            if (service == null)
            {
                return(AsyncCompletionData.CompletionStartData.DoesNotParticipateInCompletion);
            }

            if (!document.Project.Solution.Workspace.Options.GetOption(CompletionOptions.BlockForCompletionItems, service.Language))
            {
                _textView.Options.GlobalOptions.SetOptionValue(NonBlockingCompletionEditorOption, true);
            }

            // In case of calls with multiple completion services for the same view (e.g. TypeScript and C#), those completion services must not be called simultaneously for the same session.
            // Therefore, in each completion session we use a list of commit character for a specific completion service and a specific content type.
            _textView.Properties[PotentialCommitCharacters] = service.GetRules().DefaultCommitCharacters;

            // For telemetry reporting during the completion session
            var experimentationService = document.Project.Solution.Workspace.Services.GetService <IExperimentationService>();

            _textView.Properties[TargetTypeFilterExperimentEnabled] = experimentationService.IsExperimentEnabled(WellKnownExperimentNames.TargetTypedCompletionFilter);

            var sourceText = document.GetTextSynchronously(cancellationToken);

            return(ShouldTriggerCompletion(trigger, triggerLocation, sourceText, document, service)
                ? new AsyncCompletionData.CompletionStartData(
                       participation: AsyncCompletionData.CompletionParticipation.ProvidesItems,
                       applicableToSpan: new SnapshotSpan(
                           triggerLocation.Snapshot,
                           service.GetDefaultCompletionListSpan(sourceText, triggerLocation.Position).ToSpan()))
                : AsyncCompletionData.CompletionStartData.DoesNotParticipateInCompletion);
        }
示例#6
0
        internal static CompletionFilterReason GetFilterReason(AsyncCompletionData.CompletionTrigger trigger)
        {
            switch (trigger.Reason)
            {
            case AsyncCompletionData.CompletionTriggerReason.Insertion:
                return(CompletionFilterReason.Insertion);

            case AsyncCompletionData.CompletionTriggerReason.Deletion:
            case AsyncCompletionData.CompletionTriggerReason.Backspace:
                return(CompletionFilterReason.Deletion);

            default:
                return(CompletionFilterReason.Other);
            }
        }
示例#7
0
        internal static CompletionTriggerKind GetRoslynTriggerKind(EditorAsyncCompletionData.CompletionTrigger trigger)
        {
            switch (trigger.Reason)
            {
            case EditorAsyncCompletionData.CompletionTriggerReason.InvokeAndCommitIfUnique:
                return(CompletionTriggerKind.InvokeAndCommitIfUnique);

            case EditorAsyncCompletionData.CompletionTriggerReason.Insertion:
                return(CompletionTriggerKind.Insertion);

            case EditorAsyncCompletionData.CompletionTriggerReason.Deletion:
            case EditorAsyncCompletionData.CompletionTriggerReason.Backspace:
                return(CompletionTriggerKind.Deletion);

            case EditorAsyncCompletionData.CompletionTriggerReason.SnippetsMode:
                return(CompletionTriggerKind.Snippets);

            default:
                return(CompletionTriggerKind.Invoke);
            }
        }
示例#8
0
        public async Task <AsyncCompletionData.CompletionContext> GetCompletionContextAsync(
            IAsyncCompletionSession session,
            AsyncCompletionData.CompletionTrigger trigger,
            SnapshotPoint triggerLocation,
            SnapshotSpan applicableToSpan,
            CancellationToken cancellationToken)
        {
            var document = triggerLocation.Snapshot.GetOpenDocumentInCurrentContextWithChanges();

            if (document == null)
            {
                return(new AsyncCompletionData.CompletionContext(ImmutableArray <VSCompletionItem> .Empty));
            }

            var completionService = document.GetLanguageService <CompletionService>();

            var roslynTrigger = Helpers.GetRoslynTrigger(trigger, triggerLocation);

            var workspace = document.Project.Solution.Workspace;

            var completionList = await completionService.GetCompletionsAsync(
                document,
                triggerLocation,
                roslynTrigger,
                _roles,
                _isDebuggerTextView?workspace.Options.WithDebuggerCompletionOptions() : workspace.Options,
                    cancellationToken).ConfigureAwait(false);

            if (completionList == null)
            {
                return(new AsyncCompletionData.CompletionContext(ImmutableArray <VSCompletionItem> .Empty));
            }

            var itemsBuilder = new ArrayBuilder <VSCompletionItem>(completionList.Items.Length);

            foreach (var roslynItem in completionList.Items)
            {
                cancellationToken.ThrowIfCancellationRequested();
                var item = Convert(document, roslynItem);
                itemsBuilder.Add(item);
            }

            var items = itemsBuilder.ToImmutableAndFree();

            var suggestionItemOptions = completionList.SuggestionModeItem != null
                    ? new AsyncCompletionData.SuggestionItemOptions(
                completionList.SuggestionModeItem.DisplayText,
                completionList.SuggestionModeItem.Properties.TryGetValue(Description, out var description)
                            ? description
                            : string.Empty)
                    : null;

            // Have to store the snapshot to reuse it in some projections related scenarios
            // where data and session in further calls are able to provide other snapshots.
            session.Properties.AddProperty(TriggerSnapshot, triggerLocation.Snapshot);

            // Store around the span this completion list applies to.  We'll use this later
            // to pass this value in when we're committing a completion list item.
            session.Properties.AddProperty(CompletionListSpan, completionList.Span);

            // This is a code supporting original completion scenarios:
            // Controller.Session_ComputeModel: if completionList.SuggestionModeItem != null, then suggestionMode = true
            // If there are suggestionItemOptions, then later HandleNormalFiltering should set selection to SoftSelection.
            session.Properties.AddProperty(HasSuggestionItemOptions, suggestionItemOptions != null);

            session.Properties.AddProperty(InitialTriggerKind, roslynTrigger.Kind);
            var excludedCommitCharacters = GetExcludedCommitCharacters(completionList.Items);

            if (excludedCommitCharacters.Length > 0)
            {
                session.Properties.AddProperty(ExcludedCommitCharacters, excludedCommitCharacters);
            }

            return(new AsyncCompletionData.CompletionContext(
                       items,
                       suggestionItemOptions,
                       suggestionItemOptions == null
                    ? AsyncCompletionData.InitialSelectionHint.RegularSelection
                    : AsyncCompletionData.InitialSelectionHint.SoftSelection));
        }