public CompletionListUpdater( ITrackingSpan applicableToSpan, CompletionSessionData sessionData, AsyncCompletionSessionDataSnapshot snapshotData, RecentItemsManager recentItemsManager, IGlobalOptionService globalOptions) { _sessionData = sessionData; _snapshotData = snapshotData; _recentItemsManager = recentItemsManager; _applicableToSpan = applicableToSpan; _filterText = applicableToSpan.GetText(_snapshotData.Snapshot); _hasSuggestedItemOptions = _sessionData.HasSuggestionItemOptions || _snapshotData.DisplaySuggestionItem; // We prefer using the original snapshot, which should always be available from items provided by Roslyn's CompletionSource. // Only use data.Snapshot in the theoretically possible but rare case when all items we are handling are from some non-Roslyn CompletionSource. var snapshotForDocument = TryGetInitialTriggerLocation(_snapshotData, out var intialTriggerLocation) ? intialTriggerLocation.Snapshot : _snapshotData.Snapshot; _document = snapshotForDocument?.TextBuffer.AsTextContainer().GetOpenDocumentInCurrentContext(); if (_document != null) { _completionService = _document.GetLanguageService <CompletionService>(); _completionRules = _completionService?.GetRules(globalOptions.GetCompletionOptions(_document.Project.Language)) ?? CompletionRules.Default; // Let us make the completion Helper used for non-Roslyn items case-sensitive. // We can change this if get requests from partner teams. _completionHelper = CompletionHelper.GetHelper(_document); _filterMethod = _completionService == null ? ((itemsWithPatternMatches, text) => CompletionService.FilterItems(_completionHelper, itemsWithPatternMatches, text)) : ((itemsWithPatternMatches, text) => _completionService.FilterItems(_document, itemsWithPatternMatches, text)); // Nothing to highlight if user hasn't typed anything yet. _highlightMatchingPortions = _filterText.Length > 0 && globalOptions.GetOption(CompletionViewOptions.HighlightMatchingPortionsOfCompletionListItems, _document.Project.Language); _showCompletionItemFilters = globalOptions.GetOption(CompletionViewOptions.ShowCompletionItemFilters, _document.Project.Language); } else { _completionService = null; _completionRules = CompletionRules.Default; // Let us make the completion Helper used for non-Roslyn items case-sensitive. // We can change this if get requests from partner teams. _completionHelper = new CompletionHelper(isCaseSensitive: true); _filterMethod = (itemsWithPatternMatches, text) => CompletionService.FilterItems(_completionHelper, itemsWithPatternMatches, text); _highlightMatchingPortions = false; _showCompletionItemFilters = true; } }
private CompletionHelper GetCompletionHelper() { var document = GetDocument(); if (document != null) { return(CompletionHelper.GetHelper(document)); } return(null); }
private CompletionHelper GetCompletionHelper() { var document = this.SubjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); if (document != null) { return(CompletionHelper.GetHelper(document)); } return(null); }
public async Task <CompletionResult> CompleteAsync(string sourceCode, int position, char?triggerChar) { _CancellationTokenSource?.Cancel(); _CancellationTokenSource?.Dispose(); _CancellationTokenSource = new CancellationTokenSource(); try { var workspace = new AdhocWorkspace(_Host); var projectInfo = ProjectInfo .Create(ProjectId.CreateNewId(), VersionStamp.Create(), "Project", "Project", LanguageNames.CSharp) .WithMetadataReferences(new[] { MetadataReference.CreateFromFile(typeof(object).Assembly.Location) }); var project = workspace.AddProject(projectInfo); var document = workspace.AddDocument(project.Id, "File.cs", SourceText.From(sourceCode)); var completionService = CompletionService.GetService(document); var completionTrigger = GetCompletionTrigger(triggerChar); var data = await completionService.GetCompletionsAsync(document, position, completionTrigger, null, null, _CancellationTokenSource.Token) .ConfigureAwait(false); if (data == null || data.Items == null) { return(new CompletionResult(Array.Empty <CompleteData>())); } var helper = CompletionHelper.GetHelper(document); var text = await document.GetTextAsync(_CancellationTokenSource.Token).ConfigureAwait(false); var textSpanToText = new Dictionary <TextSpan, string>(); var items = data.Items .Where(item => MatchesFilterText(helper, item, text, textSpanToText)) .OrderBy(x => x.DisplayText) .Distinct(x => x.DisplayText) .Select(x => new CompleteData( x, completionService, document) ).ToArray(); return(new CompletionResult(items)); } catch (OperationCanceledException) { return(new CompletionResult(Array.Empty <CompleteData>())); } }
private CompletionHelper GetCompletionHelper() { _foregroundObject.AssertIsForeground(); if (_completionHelper == null) { var document = _subjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); if (document != null) { _completionHelper = CompletionHelper.GetHelper(document); } } return(_completionHelper); }
private CompletionHelper GetCompletionHelper() { this.AssertIsForeground(); if (_completionHelper == null) { var document = GetDocument(); if (document != null) { _completionHelper = CompletionHelper.GetHelper(document); } } return(_completionHelper); }
private CompletionHelper GetCompletionHelper() { this.AssertIsForeground(); if (_completionHelper == null) { var document = GetDocument(); if (document != null) { _completionHelper = CompletionHelper.GetHelper(document, document.Project.LanguageServices.GetService <CompletionService>()); } } return(_completionHelper); }
/// <summary> /// Start a new ModelComputation. Completion computations and filtering tasks will be chained to this /// ModelComputation, and when the chain is complete the view will be notified via the /// OnCompletionModelUpdated handler. /// /// The latest immutable CompletionModel can be accessed at any time. Some parts of the code may choose /// to wait on it to arrive synchronously. /// /// Inspired by similar method in Completion/Controller.cs in Roslyn. /// </summary> void StartNewComputation( int position, CompletionRules rules, bool filterItems) { computation = new ModelComputation <CompletionModel> ( OnCompletionModelUpdated, Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion.PrioritizedTaskScheduler.AboveNormalInstance); ComputeModel(position); if (filterItems) { var document = compilationWorkspace.GetSubmissionDocument(sourceTextContent.Container); FilterModel(CompletionHelper.GetHelper(document)); } }
public async Task <CompleteData[]> CompleteAsync(string sourceCode, int position, char?triggerChar) { _CancellationTokenSource?.Cancel(); _CancellationTokenSource?.Dispose(); _CancellationTokenSource = new CancellationTokenSource(); var sourceText = SourceText.From(sourceCode); var document = _project.AddDocument("File.cs", sourceText); _project = document.Project; try { var completionService = CompletionService.GetService(document); var completionTrigger = GetCompletionTrigger(triggerChar); var data = await completionService .GetCompletionsAsync(document, position, completionTrigger, null, null, _CancellationTokenSource.Token) .ConfigureAwait(false); if (data == null || data.Items == null) { return(Array.Empty <CompleteData>()); } var helper = CompletionHelper.GetHelper(document); var textSpanToText = new Dictionary <TextSpan, string>(); return(data.Items .Where(item => MatchesFilterText(helper, item, sourceText, textSpanToText)) .Distinct(x => x.DisplayText) .Select(x => new CompleteData(x, completionService, document)) .ToArray()); } catch (OperationCanceledException) { return(Array.Empty <CompleteData>()); } finally { _project = _project.RemoveDocument(document.Id); } }
protected async Task VerifySendEnterThroughToEnterAsync(string initialMarkup, string textTypedSoFar, bool sendThroughEnterEnabled, bool expected) { using (var workspace = await TestWorkspace.CreateCSharpAsync(initialMarkup)) { var hostDocument = workspace.DocumentWithCursor; var documentId = workspace.GetDocumentId(hostDocument); var document = workspace.CurrentSolution.GetDocument(documentId); var position = hostDocument.CursorPosition.Value; var completionList = await GetCompletionListAsync(document, position, CompletionTrigger.Default); var item = completionList.Items.First(i => i.DisplayText.StartsWith(textTypedSoFar)); var optionService = workspace.Services.GetService <IOptionService>(); var options = optionService.GetOptions().WithChangedOption(CSharpCompletionOptions.AddNewLineOnEnterAfterFullyTypedWord, sendThroughEnterEnabled); optionService.SetOptions(options); var completionRules = CompletionHelper.GetHelper(document); Assert.Equal(expected, completionRules.SendEnterThroughToEditor(item, textTypedSoFar, workspace.Options)); } }
private FilteredCompletionModel UpdateCompletionList( IAsyncCompletionSession session, AsyncCompletionSessionDataSnapshot data, CancellationToken cancellationToken) { if (!session.Properties.TryGetProperty(CompletionSource.HasSuggestionItemOptions, out bool hasSuggestedItemOptions)) { // This is the scenario when the session is created out of Roslyn, in some other provider, e.g. in Debugger. // For now, the default hasSuggestedItemOptions is false. hasSuggestedItemOptions = false; } hasSuggestedItemOptions |= data.DisplaySuggestionItem; var filterText = session.ApplicableToSpan.GetText(data.Snapshot); var reason = data.Trigger.Reason; if (!session.Properties.TryGetProperty(CompletionSource.InitialTriggerKind, out CompletionTriggerKind initialRoslynTriggerKind)) { initialRoslynTriggerKind = CompletionTriggerKind.Invoke; } // Check if the user is typing a number. If so, only proceed if it's a number // directly after a <dot>. That's because it is actually reasonable for completion // to be brought up after a <dot> and for the user to want to filter completion // items based on a number that exists in the name of the item. However, when // we are not after a dot (i.e. we're being brought up after <space> is typed) // then we don't want to filter things. Consider the user writing: // // dim i =<space> // // We'll bring up the completion list here (as VB has completion on <space>). // If the user then types '3', we don't want to match against Int32. if (filterText.Length > 0 && char.IsNumber(filterText[0])) { if (!IsAfterDot(data.Snapshot, session.ApplicableToSpan)) { // Dismiss the session. return(null); } } // We need to filter if a non-empty strict subset of filters are selected var selectedFilters = data.SelectedFilters.Where(f => f.IsSelected).Select(f => f.Filter).ToImmutableArray(); var needToFilter = selectedFilters.Length > 0 && selectedFilters.Length < data.SelectedFilters.Length; var filterReason = Helpers.GetFilterReason(data.Trigger); // If the session was created/maintained out of Roslyn, e.g. in debugger; no properties are set and we should use data.Snapshot. // However, we prefer using the original snapshot in some projection scenarios. if (!session.Properties.TryGetProperty(CompletionSource.TriggerSnapshot, out ITextSnapshot snapshotForDocument)) { snapshotForDocument = data.Snapshot; } var document = snapshotForDocument.TextBuffer.AsTextContainer().GetOpenDocumentInCurrentContext(); var completionService = document?.GetLanguageService <CompletionService>(); var completionRules = completionService?.GetRules() ?? CompletionRules.Default; var completionHelper = document != null?CompletionHelper.GetHelper(document) : _defaultCompletionHelper; var initialListOfItemsToBeIncluded = new List <ExtendedFilterResult>(); foreach (var item in data.InitialSortedList) { cancellationToken.ThrowIfCancellationRequested(); if (needToFilter && ShouldBeFilteredOutOfCompletionList(item, selectedFilters)) { continue; } if (!item.Properties.TryGetProperty(CompletionSource.RoslynItem, out RoslynCompletionItem roslynItem)) { roslynItem = RoslynCompletionItem.Create( displayText: item.DisplayText, filterText: item.FilterText, sortText: item.SortText, displayTextSuffix: item.Suffix); } if (MatchesFilterText(completionHelper, roslynItem, filterText, initialRoslynTriggerKind, filterReason, _recentItemsManager.RecentItems)) { initialListOfItemsToBeIncluded.Add(new ExtendedFilterResult(item, new FilterResult(roslynItem, filterText, matchedFilterText: true))); } else { // The item didn't match the filter text. We'll still keep it in the list // if one of two things is true: // // 1. The user has only typed a single character. In this case they might // have just typed the character to get completion. Filtering out items // here is not desirable. // // 2. They brough up completion with ctrl-j or through deletion. In these // cases we just always keep all the items in the list. if (initialRoslynTriggerKind == CompletionTriggerKind.Deletion || initialRoslynTriggerKind == CompletionTriggerKind.Invoke || filterText.Length <= 1) { initialListOfItemsToBeIncluded.Add(new ExtendedFilterResult(item, new FilterResult(roslynItem, filterText, matchedFilterText: false))); } } } if (data.Trigger.Reason == CompletionTriggerReason.Backspace && completionRules.DismissIfLastCharacterDeleted && session.ApplicableToSpan.GetText(data.Snapshot).Length == 0) { // Dismiss the session return(null); } if (initialListOfItemsToBeIncluded.Count == 0) { return(HandleAllItemsFilteredOut(reason, data.SelectedFilters, completionRules)); } var options = document?.Project.Solution.Options; var highlightMatchingPortions = options?.GetOption(CompletionOptions.HighlightMatchingPortionsOfCompletionListItems, document.Project.Language) ?? true; var showCompletionItemFilters = options?.GetOption(CompletionOptions.ShowCompletionItemFilters, document.Project.Language) ?? true; var updatedFilters = showCompletionItemFilters ? GetUpdatedFilters(initialListOfItemsToBeIncluded, data.SelectedFilters) : ImmutableArray <CompletionFilterWithState> .Empty; var highlightedList = GetHighlightedList(initialListOfItemsToBeIncluded, filterText, completionHelper, highlightMatchingPortions).ToImmutableArray(); // If this was deletion, then we control the entire behavior of deletion ourselves. if (initialRoslynTriggerKind == CompletionTriggerKind.Deletion) { return(HandleDeletionTrigger(data.Trigger.Reason, initialListOfItemsToBeIncluded, filterText, updatedFilters, highlightedList)); } Func <ImmutableArray <RoslynCompletionItem>, string, ImmutableArray <RoslynCompletionItem> > filterMethod; if (completionService == null) { filterMethod = (items, text) => CompletionService.FilterItems(completionHelper, items, text); } else { filterMethod = (items, text) => completionService.FilterItems(document, items, text); } return(HandleNormalFiltering( filterMethod, filterText, updatedFilters, initialRoslynTriggerKind, filterReason, data.Trigger.Character, initialListOfItemsToBeIncluded, highlightedList, completionHelper, hasSuggestedItemOptions)); }
private FilteredCompletionModel UpdateCompletionList( IAsyncCompletionSession session, AsyncCompletionSessionDataSnapshot data, CancellationToken cancellationToken) { if (!session.Properties.TryGetProperty(CompletionSource.HasSuggestionItemOptions, out bool hasSuggestedItemOptions)) { // This is the scenario when the session is created out of Roslyn, in some other provider, e.g. in Debugger. // For now, the default hasSuggestedItemOptions is false. hasSuggestedItemOptions = false; } hasSuggestedItemOptions |= data.DisplaySuggestionItem; var filterText = session.ApplicableToSpan.GetText(data.Snapshot); var reason = data.Trigger.Reason; var initialRoslynTriggerKind = Helpers.GetRoslynTriggerKind(data.InitialTrigger); // Check if the user is typing a number. If so, only proceed if it's a number // directly after a <dot>. That's because it is actually reasonable for completion // to be brought up after a <dot> and for the user to want to filter completion // items based on a number that exists in the name of the item. However, when // we are not after a dot (i.e. we're being brought up after <space> is typed) // then we don't want to filter things. Consider the user writing: // // dim i =<space> // // We'll bring up the completion list here (as VB has completion on <space>). // If the user then types '3', we don't want to match against Int32. if (filterText.Length > 0 && char.IsNumber(filterText[0])) { if (!IsAfterDot(data.Snapshot, session.ApplicableToSpan)) { // Dismiss the session. return(null); } } // We need to filter if // 1. a non-empty strict subset of filters are selected // 2. a non-empty set of expanders are unselected var nonExpanderFilterStates = data.SelectedFilters.WhereAsArray(f => !(f.Filter is CompletionExpander)); var selectedNonExpanderFilters = nonExpanderFilterStates.Where(f => f.IsSelected).SelectAsArray(f => f.Filter); var needToFilter = selectedNonExpanderFilters.Length > 0 && selectedNonExpanderFilters.Length < nonExpanderFilterStates.Length; var unselectedExpanders = data.SelectedFilters.Where(f => !f.IsSelected && f.Filter is CompletionExpander).SelectAsArray(f => f.Filter); var needToFilterExpanded = unselectedExpanders.Length > 0; if (session.TextView.Properties.TryGetProperty(CompletionSource.TargetTypeFilterExperimentEnabled, out bool isExperimentEnabled) && isExperimentEnabled) { // Telemetry: Want to know % of sessions with the "Target type matches" filter where that filter is actually enabled if (needToFilter && !session.Properties.ContainsProperty(_targetTypeCompletionFilterChosenMarker) && selectedNonExpanderFilters.Any(f => f.DisplayText == FeaturesResources.Target_type_matches)) { AsyncCompletionLogger.LogTargetTypeFilterChosenInSession(); // Make sure we only record one enabling of the filter per session session.Properties.AddProperty(_targetTypeCompletionFilterChosenMarker, _targetTypeCompletionFilterChosenMarker); } } var filterReason = Helpers.GetFilterReason(data.Trigger); // If the session was created/maintained out of Roslyn, e.g. in debugger; no properties are set and we should use data.Snapshot. // However, we prefer using the original snapshot in some projection scenarios. var snapshotForDocument = Helpers.TryGetInitialTriggerLocation(session, out var triggerLocation) ? triggerLocation.Snapshot : data.Snapshot; var document = snapshotForDocument.TextBuffer.AsTextContainer().GetOpenDocumentInCurrentContext(); var completionService = document?.GetLanguageService <CompletionService>(); var completionRules = completionService?.GetRules() ?? CompletionRules.Default; var completionHelper = document != null?CompletionHelper.GetHelper(document) : _defaultCompletionHelper; // DismissIfLastCharacterDeleted should be applied only when started with Insertion, and then Deleted all characters typed. // This conforms with the original VS 2010 behavior. if (initialRoslynTriggerKind == CompletionTriggerKind.Insertion && data.Trigger.Reason == CompletionTriggerReason.Backspace && completionRules.DismissIfLastCharacterDeleted && session.ApplicableToSpan.GetText(data.Snapshot).Length == 0) { // Dismiss the session return(null); } var options = document?.Project.Solution.Options; var highlightMatchingPortions = options?.GetOption(CompletionOptions.HighlightMatchingPortionsOfCompletionListItems, document.Project.Language) ?? false; // Nothing to highlight if user hasn't typed anything yet. highlightMatchingPortions = highlightMatchingPortions && filterText.Length > 0; // Use a monotonically increasing integer to keep track the original alphabetical order of each item. var currentIndex = 0; var builder = ArrayBuilder <MatchResult> .GetInstance(); foreach (var item in data.InitialSortedList) { cancellationToken.ThrowIfCancellationRequested(); if (needToFilter && ShouldBeFilteredOutOfCompletionList(item, selectedNonExpanderFilters)) { continue; } if (needToFilterExpanded && ShouldBeFilteredOutOfExpandedCompletionList(item, unselectedExpanders)) { continue; } if (TryCreateMatchResult( completionHelper, item, filterText, initialRoslynTriggerKind, filterReason, _recentItemsManager.RecentItems, highlightMatchingPortions: highlightMatchingPortions, ref currentIndex, out var matchResult)) { builder.Add(matchResult); } } if (builder.Count == 0) { return(HandleAllItemsFilteredOut(reason, data.SelectedFilters, completionRules)); } // Sort the items by pattern matching results. // Note that we want to preserve the original alphabetical order for items with same pattern match score, // but `ArrayBuilder.Sort` isn't stable. Therefore we have to add a monotonically increasing integer // to `MatchResult` to archieve this. builder.Sort(MatchResult.SortingComparer); var initialListOfItemsToBeIncluded = builder.ToImmutableAndFree(); var showCompletionItemFilters = options?.GetOption(CompletionOptions.ShowCompletionItemFilters, document.Project.Language) ?? true; var updatedFilters = showCompletionItemFilters ? GetUpdatedFilters(initialListOfItemsToBeIncluded, data.SelectedFilters) : ImmutableArray <CompletionFilterWithState> .Empty; // If this was deletion, then we control the entire behavior of deletion ourselves. if (initialRoslynTriggerKind == CompletionTriggerKind.Deletion) { return(HandleDeletionTrigger(data.Trigger.Reason, initialListOfItemsToBeIncluded, filterText, updatedFilters)); } Func <ImmutableArray <(RoslynCompletionItem, PatternMatch?)>, string, ImmutableArray <RoslynCompletionItem> > filterMethod; if (completionService == null) { filterMethod = (itemsWithPatternMatches, text) => CompletionService.FilterItems(completionHelper, itemsWithPatternMatches); } else { filterMethod = (itemsWithPatternMatches, text) => completionService.FilterItems(document, itemsWithPatternMatches, text); } return(HandleNormalFiltering( filterMethod, filterText, updatedFilters, filterReason, data.Trigger.Character, initialListOfItemsToBeIncluded, hasSuggestedItemOptions));
private CompletionHelper GetCompletionHelper() { var document = GetDocument(); return(CompletionHelper.GetHelper(document)); }
public async Task <CompletionResult> GetCompletionData(int position, char?triggerChar, bool useSignatureHelp) { IList <ICompletionDataEx> completionData = null; IOverloadProviderEx overloadProvider = null; var useHardSelection = true; var document = _roslynHost.GetDocument(_documentId); if (useSignatureHelp || triggerChar != null) { var signatureHelpProvider = _roslynHost.GetService <ISignatureHelpProvider>(); var isSignatureHelp = useSignatureHelp || signatureHelpProvider.IsTriggerCharacter(triggerChar.Value); if (isSignatureHelp) { var signatureHelp = await signatureHelpProvider.GetItemsAsync( document, position, new SignatureHelpTriggerInfo( useSignatureHelp ? SignatureHelpTriggerReason.InvokeSignatureHelpCommand : SignatureHelpTriggerReason.TypeCharCommand, triggerChar)) .ConfigureAwait(false); if (signatureHelp != null) { overloadProvider = new RoslynOverloadProvider(signatureHelp); } } } if (overloadProvider == null) { var completionService = CompletionService.GetService(document); var completionTrigger = GetCompletionTrigger(triggerChar); var data = await completionService.GetCompletionsAsync( document, position, completionTrigger ).ConfigureAwait(false); if (data != null && data.Items.Any()) { useHardSelection = data.SuggestionModeItem == null; var helper = CompletionHelper.GetHelper(document, completionService); var text = await document.GetTextAsync().ConfigureAwait(false); var textSpanToText = new Dictionary <TextSpan, string>(); completionData = data.Items .Where(item => MatchesFilterText(helper, item, text, textSpanToText)) .Select(item => new RoslynCompletionData(document, item, triggerChar, _snippetService.SnippetManager)) .ToArray <ICompletionDataEx>(); } else { completionData = Array.Empty <ICompletionDataEx>(); } } return(new CompletionResult(completionData, overloadProvider, useHardSelection)); }
private CompletionHelper GetCompletionHelper(Document document) { return(CompletionHelper.GetHelper(document)); }
internal static CompletionHelper GetCompletionHelper(Document document) { return(CompletionHelper.GetHelper(document)); }
internal static CompletionHelper GetCompletionHelper(Document document, CompletionService service) { return(CompletionHelper.GetHelper(document, service)); }