|
|
|
@ -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
|
|
|
|
// SelectItem gets called twice for every typed character (once from FormatLine), this helps execute SelectItem only once
|
|
|
|
string currentText; |
|
|
|
string currentText; |
|
|
|
ObservableCollection<ICompletionData> currentList; |
|
|
|
ObservableCollection<ICompletionData> currentList; |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// <summary>
|
|
|
|
/// Selects the best match, and filter the items if turned on using <see cref="IsFiltering" />.
|
|
|
|
/// Selects the best match, and filter the items if turned on using <see cref="IsFiltering" />.
|
|
|
|
/// </summary>
|
|
|
|
/// </summary>
|
|
|
|
@ -219,29 +219,32 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// <summary>
|
|
|
|
/// 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.
|
|
|
|
/// </summary>
|
|
|
|
/// </summary>
|
|
|
|
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
|
|
|
|
// 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)) && |
|
|
|
var listToFilter = (this.currentList != null && (!string.IsNullOrEmpty(this.currentText)) && (!string.IsNullOrEmpty(query)) && |
|
|
|
text.StartsWith(this.currentText)) ? |
|
|
|
query.StartsWith(this.currentText)) ? |
|
|
|
this.currentList : this.completionData; |
|
|
|
this.currentList : this.completionData; |
|
|
|
|
|
|
|
|
|
|
|
var itemsWithQualities = |
|
|
|
var itemsWithQualities = |
|
|
|
from item in listToFilter |
|
|
|
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); |
|
|
|
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<ICompletionData>(); |
|
|
|
var listBoxItems = new ObservableCollection<ICompletionData>(); |
|
|
|
int bestIndex = -1; |
|
|
|
int bestIndex = -1; |
|
|
|
int bestQuality = -1; |
|
|
|
int bestQuality = -1; |
|
|
|
double bestPriority = 0; |
|
|
|
double bestPriority = 0; |
|
|
|
int i = 0; |
|
|
|
int i = 0; |
|
|
|
foreach (var matchingItem in matchingItems) { |
|
|
|
foreach (var matchingItem in matchingItems) { |
|
|
|
double priority = matchingItem.Item.Priority; |
|
|
|
double priority = matchingItem.Item == suggestedItem ? double.PositiveInfinity : matchingItem.Item.Priority; |
|
|
|
int quality = matchingItem.Quality; |
|
|
|
int quality = matchingItem.Quality; |
|
|
|
if (quality > bestQuality || (quality == bestQuality && priority > bestPriority)) { |
|
|
|
if (quality > bestQuality || (quality == bestQuality && (priority > bestPriority))) { |
|
|
|
bestIndex = i; |
|
|
|
bestIndex = i; |
|
|
|
bestPriority = priority; |
|
|
|
bestPriority = priority; |
|
|
|
bestQuality = quality; |
|
|
|
bestQuality = quality; |
|
|
|
@ -255,20 +258,20 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// <summary>
|
|
|
|
/// Selects the item that starts with the specified text.
|
|
|
|
/// Selects the item that starts with the specified query.
|
|
|
|
/// </summary>
|
|
|
|
/// </summary>
|
|
|
|
void SelectItemWithStart(string startText) |
|
|
|
void SelectItemWithStart(string query) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (string.IsNullOrEmpty(startText)) |
|
|
|
if (string.IsNullOrEmpty(query)) |
|
|
|
return; |
|
|
|
return; |
|
|
|
|
|
|
|
|
|
|
|
int selectedItem = listBox.SelectedIndex; |
|
|
|
int suggestedIndex = listBox.SelectedIndex; |
|
|
|
|
|
|
|
|
|
|
|
int bestIndex = -1; |
|
|
|
int bestIndex = -1; |
|
|
|
int bestQuality = -1; |
|
|
|
int bestQuality = -1; |
|
|
|
double bestPriority = 0; |
|
|
|
double bestPriority = 0; |
|
|
|
for (int i = 0; i < completionData.Count; ++i) { |
|
|
|
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) |
|
|
|
if (quality < 0) |
|
|
|
continue; |
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
|
|
@ -277,10 +280,10 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion |
|
|
|
if (bestQuality < quality) { |
|
|
|
if (bestQuality < quality) { |
|
|
|
useThisItem = true; |
|
|
|
useThisItem = true; |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
if (bestIndex == selectedItem) { |
|
|
|
if (bestIndex == suggestedIndex) { |
|
|
|
// martin.konicek: I'm not sure what this does. This item could have higher priority, why not select it?
|
|
|
|
|
|
|
|
useThisItem = false; |
|
|
|
useThisItem = false; |
|
|
|
} else if (i == selectedItem) { |
|
|
|
} else if (i == suggestedIndex) { |
|
|
|
|
|
|
|
// prefer recommendedItem, regardless of its priority
|
|
|
|
useThisItem = bestQuality == quality; |
|
|
|
useThisItem = bestQuality == quality; |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
useThisItem = bestQuality == quality && bestPriority < priority; |
|
|
|
useThisItem = bestQuality == quality && bestPriority < priority; |
|
|
|
@ -314,15 +317,15 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion |
|
|
|
int GetMatchQuality(string itemText, string query) |
|
|
|
int GetMatchQuality(string itemText, string query) |
|
|
|
{ |
|
|
|
{ |
|
|
|
// Qualities:
|
|
|
|
// 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
|
|
|
|
// 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) |
|
|
|
if (query == itemText) |
|
|
|
return 8; |
|
|
|
return 8; |
|
|
|
if (string.Equals(itemText, query, StringComparison.OrdinalIgnoreCase)) |
|
|
|
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
|
|
|
|
// search by substring, if filtering (i.e. new behavior) turned on
|
|
|
|
if (IsFiltering && itemText.Contains(query)) |
|
|
|
if (IsFiltering) { |
|
|
|
return 3; |
|
|
|
if (itemText.Contains(query)) |
|
|
|
if (IsFiltering && itemText.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0) |
|
|
|
return 3; |
|
|
|
return 2; |
|
|
|
if (itemText.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0) |
|
|
|
|
|
|
|
return 2; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!camelCaseMatch.HasValue) |
|
|
|
if (!camelCaseMatch.HasValue) |
|
|
|
camelCaseMatch = CamelCaseMatch(itemText, query); |
|
|
|
camelCaseMatch = CamelCaseMatch(itemText, query); |
|
|
|
if (camelCaseMatch.GetValueOrDefault(false)) |
|
|
|
if (camelCaseMatch.GetValueOrDefault(false)) |
|
|
|
@ -358,7 +363,7 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion |
|
|
|
int i = 0; |
|
|
|
int i = 0; |
|
|
|
foreach (char upper in text.Where(c => char.IsUpper(c))) { |
|
|
|
foreach (char upper in text.Where(c => char.IsUpper(c))) { |
|
|
|
if (i > query.Length - 1) |
|
|
|
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) |
|
|
|
if (char.ToUpper(query[i]) != upper) |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
i++; |
|
|
|
i++; |
|
|
|
|