/// <summary> /// Gets a code completion matcher for the provided string. It will try to reuse an existing one. /// </summary> CompletionDataMatcher GetCompletionDataMatcher(string partialWord) { // It keeps the last used matcher in a field, and will reuse it if the requested word doesn't change. // 'partialWord' doesn't usually change during the lifetime of CompletionDataList, so in general // the same matcher will be used by all calculations required for a key press if (lastMatcher != null && partialWord == lastMatcher.MatchString) { return(lastMatcher); } return(lastMatcher = new CompletionDataMatcher { MatcherId = lastMatcherId++, StringMatcher = CompletionMatcher.CreateCompletionMatcher(partialWord), MatchString = partialWord }); }
internal static CompletionListFilterResult DefaultFilterItems(ICompletionDataList dataList, IReadOnlyList <int> currentFilteredItems, string oldCompletionString, string completionString, CompletionDataMatcher matcher = null) { List <int> filteredItems; var newCategories = new List <CategorizedCompletionItems> (); matcher = matcher ?? new CompletionDataMatcher { StringMatcher = CompletionMatcher.CreateCompletionMatcher(completionString), MatcherId = -1, MatchString = completionString }; if (oldCompletionString == null || !completionString.StartsWith(oldCompletionString, StringComparison.Ordinal)) { // We are filtering the list from scratch (not reusing previous results) // This is the most slow operation since we may have to filter a very large amount of items. // To improve performance, when there are many items, we try to parallelize the filtering // Use multiple threads when we can split the work in chunks of at least 4000 items. int numFilterThreads = Math.Max(Math.Min(dataList.Count / 4000, Environment.ProcessorCount / 2), 1); // When completion string is empty the matching algorithm is not executed, so there is no need to parallelize if (string.IsNullOrEmpty(completionString)) { numFilterThreads = 1; } filteredItems = new List <int> (); int slice = dataList.Count / numFilterThreads; var results = new(Task, List <int>) [numFilterThreads - 1];
public virtual CompletionSelectionStatus FindMatchedEntry(ICompletionDataList completionDataList, MruCache cache, string partialWord, List <int> filteredItems) { // default - word with highest match rating in the list. int idx = -1; if (DefaultCompletionString != null && string.IsNullOrEmpty(partialWord)) { partialWord = DefaultCompletionString; } CompletionDataMatcher matcher = null; if (!string.IsNullOrEmpty(partialWord)) { matcher = GetCompletionDataMatcher(partialWord); string bestWord = null; int bestRank = int.MinValue; int bestIndex = 0; int bestIndexPriority = int.MinValue; for (int i = 0; i < filteredItems.Count; i++) { int index = filteredItems [i]; var data = completionDataList [index]; if (bestIndexPriority > data.PriorityGroup) { continue; } int rank; if (!matcher.CalcMatchRank(data, out rank)) { continue; } if (rank > bestRank || data.PriorityGroup > bestIndexPriority) { bestWord = data.DisplayText; bestRank = rank; bestIndex = i; bestIndexPriority = data.PriorityGroup; } } if (bestWord != null) { idx = bestIndex; // exact match found. if (string.Compare(bestWord, partialWord ?? "", true) == 0) { return(new CompletionSelectionStatus(idx)); } } } CompletionData currentData; int bestMruIndex; if (idx >= 0) { currentData = completionDataList [filteredItems [idx]]; bestMruIndex = cache.GetIndex(currentData); } else { bestMruIndex = int.MaxValue; currentData = null; } for (int i = 0; i < filteredItems.Count; i++) { var mruData = completionDataList [filteredItems [i]]; int curMruIndex = cache.GetIndex(mruData); if (curMruIndex == 1) { continue; } if (curMruIndex < bestMruIndex) { int r1 = 0, r2 = 0; if (currentData == null || matcher != null && matcher.CalcMatchRank(mruData, out r1) && matcher.CalcMatchRank(currentData, out r2)) { if (r1 >= r2 || partialWord.Length == 0 || partialWord.Length == 1 && mruData.DisplayText [0] == partialWord [0]) { bestMruIndex = curMruIndex; idx = i; currentData = mruData; } } } } return(new CompletionSelectionStatus(idx)); }