diff --git a/AddIns/ICSharpCode.SharpDevelop.addin b/AddIns/ICSharpCode.SharpDevelop.addin index a000be4aed..f44fee2328 100755 --- a/AddIns/ICSharpCode.SharpDevelop.addin +++ b/AddIns/ICSharpCode.SharpDevelop.addin @@ -1455,6 +1455,7 @@ diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionList.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionList.cs index 63a227dfee..50491c288f 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionList.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionList.cs @@ -198,7 +198,7 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion // SelectItem gets called twice for every typed character (once from FormatLine), this helps execute SelectItem only once string currentText; ObservableCollection currentList; - + /// /// Selects the best match, and filter the items if turned on using . /// @@ -219,29 +219,32 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion } /// - /// Filters CompletionList items to show only those matching given text, and selects the best match. + /// Filters CompletionList items to show only those matching given query, and selects the best match. /// - void SelectItemFiltering(string text) + void SelectItemFiltering(string query) { // if the user just typed one more character, don't filter all data but just filter what we are already displaying - var listToFilter = (this.currentList != null && (!string.IsNullOrEmpty(this.currentText)) && (!string.IsNullOrEmpty(text)) && - text.StartsWith(this.currentText)) ? - this.currentList : this.completionData; + var listToFilter = (this.currentList != null && (!string.IsNullOrEmpty(this.currentText)) && (!string.IsNullOrEmpty(query)) && + query.StartsWith(this.currentText)) ? + this.currentList : this.completionData; - var itemsWithQualities = + var itemsWithQualities = from item in listToFilter - select new { Item = item, Quality = GetMatchQuality(item.Text, text) }; + select new { Item = item, Quality = GetMatchQuality(item.Text, query) }; var matchingItems = itemsWithQualities.Where(item => item.Quality > 0); + // e.g. "DateTimeKind k = (*cc here suggests DateTimeKind*)" + ICompletionData suggestedItem = listBox.SelectedIndex != -1 ? (ICompletionData)(listBox.Items[listBox.SelectedIndex]) : null; + var listBoxItems = new ObservableCollection(); int bestIndex = -1; int bestQuality = -1; double bestPriority = 0; int i = 0; foreach (var matchingItem in matchingItems) { - double priority = matchingItem.Item.Priority; + double priority = matchingItem.Item == suggestedItem ? double.PositiveInfinity : matchingItem.Item.Priority; int quality = matchingItem.Quality; - if (quality > bestQuality || (quality == bestQuality && priority > bestPriority)) { + if (quality > bestQuality || (quality == bestQuality && (priority > bestPriority))) { bestIndex = i; bestPriority = priority; bestQuality = quality; @@ -255,20 +258,20 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion } /// - /// Selects the item that starts with the specified text. + /// Selects the item that starts with the specified query. /// - void SelectItemWithStart(string startText) + void SelectItemWithStart(string query) { - if (string.IsNullOrEmpty(startText)) + if (string.IsNullOrEmpty(query)) return; - int selectedItem = listBox.SelectedIndex; + int suggestedIndex = listBox.SelectedIndex; int bestIndex = -1; int bestQuality = -1; double bestPriority = 0; for (int i = 0; i < completionData.Count; ++i) { - int quality = GetMatchQuality(completionData[i].Text, startText); + int quality = GetMatchQuality(completionData[i].Text, query); if (quality < 0) continue; @@ -277,10 +280,10 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion if (bestQuality < quality) { useThisItem = true; } else { - if (bestIndex == selectedItem) { - // martin.konicek: I'm not sure what this does. This item could have higher priority, why not select it? + if (bestIndex == suggestedIndex) { useThisItem = false; - } else if (i == selectedItem) { + } else if (i == suggestedIndex) { + // prefer recommendedItem, regardless of its priority useThisItem = bestQuality == quality; } else { useThisItem = bestQuality == quality && bestPriority < priority; @@ -314,15 +317,15 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion int GetMatchQuality(string itemText, string query) { // Qualities: - // -1 = no match - // 1 = match CamelCase - // 2 = match sustring - // 3 = match substring case sensitive - // 4 = match CamelCase when length of query is 1 or 2 characters - // 5 = match start - // 6 = match start case sensitive - // 7 = full match // 8 = full match case sensitive + // 7 = full match + // 6 = match start case sensitive + // 5 = match start + // 4 = match CamelCase when length of query is 1 or 2 characters + // 3 = match substring case sensitive + // 2 = match sustring + // 1 = match CamelCase + // -1 = no match if (query == itemText) return 8; if (string.Equals(itemText, query, StringComparison.OrdinalIgnoreCase)) @@ -340,11 +343,13 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion } // search by substring, if filtering (i.e. new behavior) turned on - if (IsFiltering && itemText.Contains(query)) - return 3; - if (IsFiltering && itemText.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0) - return 2; - + if (IsFiltering) { + if (itemText.Contains(query)) + return 3; + if (itemText.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0) + return 2; + } + if (!camelCaseMatch.HasValue) camelCaseMatch = CamelCaseMatch(itemText, query); if (camelCaseMatch.GetValueOrDefault(false)) @@ -358,7 +363,7 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion int i = 0; foreach (char upper in text.Where(c => char.IsUpper(c))) { if (i > query.Length - 1) - return true; // return true here for CamelCase partial match (CQ match CodeQualityAnalysis) + return true; // return true here for CamelCase partial match ("CQ" matches "CodeQualityAnalysis") if (char.ToUpper(query[i]) != upper) return false; i++;