Browse Source

CodeCompletion: filtered items are shown in the same order as they were before filtering. The best match is selected.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@5750 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
pull/1/head
Martin Koníček 15 years ago
parent
commit
ef7c0f1457
  1. 77
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionList.cs
  2. 4
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionListBox.cs
  3. 5
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindow.cs

77
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionList.cs

@ -28,10 +28,15 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
new FrameworkPropertyMetadata(typeof(CompletionList))); new FrameworkPropertyMetadata(typeof(CompletionList)));
} }
bool isFiltering = true;
/// <summary> /// <summary>
/// If true, enables the old behavior: no filtering, search by string.StartsWith. /// If true, the CompletionList is filtered to show only matching items. Also enables search by substring.
/// If false, enables the old behavior: no filtering, search by string.StartsWith.
/// </summary> /// </summary>
public bool IsSearchByStartOnly { get; set; } public bool IsFiltering {
get { return isFiltering; }
set { isFiltering = value; }
}
/// <summary> /// <summary>
/// Is raised when the completion list indicates that the user has chosen /// Is raised when the completion list indicates that the user has chosen
@ -175,39 +180,49 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
} }
/// <summary> /// <summary>
/// Selects the best match, and possibly filters the items. /// Selects the best match, and filter the items if turned on using <see cref="IsFiltering" />.
/// </summary> /// </summary>
public void SelectItem(string text) public void SelectItem(string text)
{ {
if (string.IsNullOrEmpty(text))
return;
if (listBox == null) if (listBox == null)
ApplyTemplate(); ApplyTemplate();
if (this.IsSearchByStartOnly) if (this.IsFiltering) {
SelectItemWithStart(text);
else
FilterMatchingItems(text); FilterMatchingItems(text);
}
else {
SelectItemWithStart(text);
}
} }
/// <summary>
/// Filters CompletionList items to show only those matching given text, and selects the best match.
/// </summary>
void FilterMatchingItems(string text) void FilterMatchingItems(string text)
{ {
// BUG Find references to itemsWithQualities returns just this one var itemsWithQualities =
// assign qualities to items from item in this.completionData
var itemsWithQualities =
from item in completionData
select new { Item = item, Quality = GetMatchQuality(item.Text, text) }; select new { Item = item, Quality = GetMatchQuality(item.Text, text) };
// take items with quality > 0, order by quality var matchingItems = itemsWithQualities.Where(item => item.Quality > 0);
var matchingOrdered = from itemWithQ in itemsWithQualities
where itemWithQ.Quality > 0 var listBoxItems = new ObservableCollection<ICompletionData>();
orderby itemWithQ.Quality descending, itemWithQ.Item.Priority descending, itemWithQ.Item.Text int bestIndex = -1;
select itemWithQ.Item; int bestQuality = -1;
var matchingItems = new ObservableCollection<ICompletionData>(); double bestPriority = 0;
foreach (var matchingItem in matchingOrdered) { int i = 0;
matchingItems.Add(matchingItem); foreach (var matchingItem in matchingItems) {
double priority = matchingItem.Item.Priority;
int quality = matchingItem.Quality;
if (quality > bestQuality || (quality == bestQuality && priority > bestPriority)) {
bestIndex = i;
bestPriority = priority;
bestQuality = quality;
}
listBoxItems.Add(matchingItem.Item);
i++;
} }
listBox.ItemsSource = matchingItems; listBox.ItemsSource = listBoxItems;
listBox.SelectIndex(0); SelectIndexCentered(bestIndex);
} }
/// <summary> /// <summary>
@ -215,6 +230,9 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
/// </summary> /// </summary>
void SelectItemWithStart(string startText) void SelectItemWithStart(string startText)
{ {
if (string.IsNullOrEmpty(startText))
return;
int selectedItem = listBox.SelectedIndex; int selectedItem = listBox.SelectedIndex;
int bestIndex = -1; int bestIndex = -1;
@ -231,6 +249,7 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
useThisItem = true; useThisItem = true;
} else { } else {
if (bestIndex == selectedItem) { if (bestIndex == selectedItem) {
// 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 == selectedItem) {
useThisItem = bestQuality == quality; useThisItem = bestQuality == quality;
@ -244,11 +263,17 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
bestQuality = quality; bestQuality = quality;
} }
} }
SelectIndexCentered(bestIndex);
}
void SelectIndexCentered(int bestIndex)
{
if (bestIndex < 0) { if (bestIndex < 0) {
listBox.ClearSelection(); listBox.ClearSelection();
} else { } else {
int firstItem = listBox.FirstVisibleItem; int firstItem = listBox.FirstVisibleItem;
if (bestIndex < firstItem || firstItem + listBox.VisibleItemCount <= bestIndex) { if (bestIndex < firstItem || firstItem + listBox.VisibleItemCount <= bestIndex) {
// CenterViewOn does nothing as CompletionListBox.ScrollViewer is null
listBox.CenterViewOn(bestIndex); listBox.CenterViewOn(bestIndex);
listBox.SelectIndex(bestIndex); listBox.SelectIndex(bestIndex);
} else { } else {
@ -264,7 +289,7 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
// 1 = match CamelCase // 1 = match CamelCase
// 2 = match sustring // 2 = match sustring
// 3 = match substring case sensitive // 3 = match substring case sensitive
// 4 = match CamelCase when length of query is only 1 or 2 characters // 4 = match CamelCase when length of query is 1 or 2 characters
// 5 = match start // 5 = match start
// 6 = match start case sensitive // 6 = match start case sensitive
// 7 = full match // 7 = full match
@ -282,10 +307,10 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
if (query.Length <= 2 && CamelCaseMatch(itemText, query)) if (query.Length <= 2 && CamelCaseMatch(itemText, query))
return 4; return 4;
// search by substring, if not turned off // search by substring, if filtering (i.e. new behavior) turned on
if (!IsSearchByStartOnly && itemText.Contains(query)) if (IsFiltering && query.Length > 1 && itemText.Contains(query))
return 3; return 3;
if (!IsSearchByStartOnly && itemText.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0) if (IsFiltering && query.Length > 1 && itemText.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0)
return 2; return 2;
if (CamelCaseMatch(itemText, query)) if (CamelCaseMatch(itemText, query))

4
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionListBox.cs

@ -38,7 +38,7 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
/// </summary> /// </summary>
public int FirstVisibleItem { public int FirstVisibleItem {
get { get {
if (scrollViewer == null) { if (scrollViewer == null || scrollViewer.ExtentHeight == 0) {
return 0; return 0;
} else { } else {
return (int)(this.Items.Count * scrollViewer.VerticalOffset / scrollViewer.ExtentHeight); return (int)(this.Items.Count * scrollViewer.VerticalOffset / scrollViewer.ExtentHeight);
@ -57,7 +57,7 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
/// </summary> /// </summary>
public int VisibleItemCount { public int VisibleItemCount {
get { get {
if (scrollViewer == null) { if (scrollViewer == null || scrollViewer.ExtentHeight == 0) {
return 10; return 10;
} else { } else {
return Math.Max( return Math.Max(

5
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindow.cs

@ -163,8 +163,11 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
{ {
int offset = this.TextArea.Caret.Offset; int offset = this.TextArea.Caret.Offset;
if (offset == this.StartOffset) { if (offset == this.StartOffset) {
if (CloseAutomatically && CloseWhenCaretAtBeginning) if (CloseAutomatically && CloseWhenCaretAtBeginning) {
Close(); Close();
} else {
completionList.SelectItem(string.Empty);
}
return; return;
} }
if (offset < this.StartOffset || offset > this.EndOffset) { if (offset < this.StartOffset || offset > this.EndOffset) {

Loading…
Cancel
Save