Browse Source

Code completion remembers frequently used items and prefers them in SelectItemWithStart().

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@225 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 20 years ago
parent
commit
9d32b99b27
  1. 60
      src/Libraries/ICSharpCode.TextEditor/Project/Src/Gui/CompletionWindow/CodeCompletionListView.cs
  2. 13
      src/Libraries/ICSharpCode.TextEditor/Project/Src/Gui/CompletionWindow/CodeCompletionWindow.cs
  3. 20
      src/Libraries/ICSharpCode.TextEditor/Project/Src/Gui/CompletionWindow/ICompletionData.cs
  4. 1
      src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
  5. 6
      src/Main/Base/Project/Src/Dom/IMember.cs
  6. 2
      src/Main/Base/Project/Src/Gui/Components/ExtTreeView/ExtTreeView.cs
  7. 60
      src/Main/Base/Project/Src/TextEditor/Gui/Editor/CompletionWindow/CodeCompletionData.cs
  8. 191
      src/Main/Base/Project/Src/TextEditor/Gui/Editor/CompletionWindow/CodeCompletionDataUsageCache.cs
  9. 13
      src/Main/Base/Project/Src/TextEditor/Gui/Editor/CompletionWindow/CommentCompletionDataProvider.cs
  10. 11
      src/Main/Base/Project/Src/TextEditor/Gui/Editor/CompletionWindow/TemplateCompletionDataProvider.cs
  11. 4
      src/Main/Base/Project/Src/TextEditor/Gui/Editor/SharpDevelopTextAreaControl.cs

60
src/Libraries/ICSharpCode.TextEditor/Project/Src/Gui/CompletionWindow/CodeCompletionListView.cs

@ -129,42 +129,44 @@ namespace ICSharpCode.TextEditor.Gui.CompletionWindow @@ -129,42 +129,44 @@ namespace ICSharpCode.TextEditor.Gui.CompletionWindow
SelectIndex(selectedItem - 1);
}
public void SelectItemWithStart(char startCh)
{
for (int i = Math.Min(selectedItem + 1, completionData.Length - 1); i < completionData.Length; ++i) {
if (completionData[i].Text.ToLower()[0] == startCh) {
SelectIndex(i);
return;
}
}
// now loop from start to current one
for (int i = 0; i < selectedItem; ++i) {
if (completionData[i].Text.ToLower()[0] == startCh) {
SelectIndex(i);
return;
}
}
// if not found leave selection as it is
Refresh();
OnSelectedItemChanged(EventArgs.Empty);
}
public void SelectItemWithStart(string startText)
{
if (startText == null || startText.Length == 0) return;
string originalStartText = startText;
startText = startText.ToLower();
int bestIndex = -1;
int bestQuality = -1;
// Qualities: 0 = match start
// 1 = match start case sensitive
// 2 = full match
// 3 = full match case sensitive
double bestPriority = 0;
for (int i = 0; i < completionData.Length; ++i) {
if (completionData[i].Text.ToLower().StartsWith(startText)) {
SelectIndex(i);
return;
string itemText = completionData[i].Text;
string lowerText = itemText.ToLower();
if (lowerText.StartsWith(startText)) {
if (i == selectedItem)
return;
double priority = completionData[i].Priority;
int quality;
if (lowerText == startText) {
if (itemText == originalStartText)
quality = 3;
else
quality = 2;
} else if (itemText.StartsWith(originalStartText)) {
quality = 1;
} else {
quality = 0;
}
if (bestQuality < quality || bestQuality == quality && bestPriority < priority) {
bestIndex = i;
bestPriority = priority;
bestQuality = quality;
}
}
}
selectedItem = -1;
Refresh();
OnSelectedItemChanged(EventArgs.Empty);
SelectIndex(bestIndex);
}
protected override void OnPaint(PaintEventArgs pe)

13
src/Libraries/ICSharpCode.TextEditor/Project/Src/Gui/CompletionWindow/CodeCompletionWindow.cs

@ -150,8 +150,7 @@ namespace ICSharpCode.TextEditor.Gui.CompletionWindow @@ -150,8 +150,7 @@ namespace ICSharpCode.TextEditor.Gui.CompletionWindow
public override bool ProcessKeyEvent(char ch)
{
if (!Char.IsLetterOrDigit(ch) && ch != '_') {
InsertSelectedItem();
return false;
return InsertSelectedItem(ch);
}
++endOffset;
return base.ProcessKeyEvent(ch);
@ -218,7 +217,7 @@ namespace ICSharpCode.TextEditor.Gui.CompletionWindow @@ -218,7 +217,7 @@ namespace ICSharpCode.TextEditor.Gui.CompletionWindow
return true;
case Keys.Tab:
case Keys.Return:
InsertSelectedItem();
InsertSelectedItem('\0');
return true;
}
return base.ProcessTextAreaKey(keyData);
@ -226,7 +225,7 @@ namespace ICSharpCode.TextEditor.Gui.CompletionWindow @@ -226,7 +225,7 @@ namespace ICSharpCode.TextEditor.Gui.CompletionWindow
void CodeCompletionListViewDoubleClick(object sender, EventArgs e)
{
InsertSelectedItem();
InsertSelectedItem('\0');
}
void CodeCompletionListViewClick(object sender, EventArgs e)
@ -244,9 +243,10 @@ namespace ICSharpCode.TextEditor.Gui.CompletionWindow @@ -244,9 +243,10 @@ namespace ICSharpCode.TextEditor.Gui.CompletionWindow
declarationViewWindow = null;
}
void InsertSelectedItem()
bool InsertSelectedItem(char ch)
{
ICompletionData data = codeCompletionListView.SelectedCompletionData;
bool result = false;
if (data != null) {
control.BeginUpdate();
@ -255,10 +255,11 @@ namespace ICSharpCode.TextEditor.Gui.CompletionWindow @@ -255,10 +255,11 @@ namespace ICSharpCode.TextEditor.Gui.CompletionWindow
control.Document.Remove(startOffset, endOffset - startOffset);
control.ActiveTextAreaControl.Caret.Position = control.Document.OffsetToPosition(startOffset);
}
data.InsertAction(control);
result = data.InsertAction(control.ActiveTextAreaControl.TextArea, ch);
control.EndUpdate();
}
Close();
return result;
}
}
}

20
src/Libraries/ICSharpCode.TextEditor/Project/Src/Gui/CompletionWindow/ICompletionData.cs

@ -29,6 +29,24 @@ namespace ICSharpCode.TextEditor.Gui.CompletionWindow @@ -29,6 +29,24 @@ namespace ICSharpCode.TextEditor.Gui.CompletionWindow
get;
}
void InsertAction(TextEditorControl control);
/// <summary>
/// Gets a priority value for the completion data item.
/// When selecting items by their start characters, the item with the highest
/// priority is selected first.
/// </summary>
double Priority {
get;
}
/// <summary>
/// Insert the element represented by the completion data into the text
/// editor.
/// </summary>
/// <param name="textArea">TextArea to insert the completion data in.</param>
/// <param name="ch">Character that should be inserted after the completion data.
/// \0 when no character should be inserted.</param>
/// <returns>Returns true when the insert action has processed the character
/// <paramref name="ch"/>; false when the character was not processed.</returns>
bool InsertAction(TextArea textArea, char ch);
}
}

1
src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj

@ -678,6 +678,7 @@ @@ -678,6 +678,7 @@
<Compile Include="Src\TextEditor\Gui\Editor\CompletionWindow\AttributesDataProvider.cs" />
<Compile Include="Src\TextEditor\Gui\Editor\CompletionWindow\CtrlSpaceCompletionDataProvider.cs" />
<Compile Include="Src\Dom\Implementations\CombinedReturnType.cs" />
<Compile Include="Src\TextEditor\Gui\Editor\CompletionWindow\CodeCompletionDataUsageCache.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Libraries\DockPanel_Src\WinFormsUI\WinFormsUI.csproj">

6
src/Main/Base/Project/Src/Dom/IMember.cs

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
// <file>
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Mike Krüger" email="mike@icsharpcode.net"/>
@ -27,6 +27,10 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -27,6 +27,10 @@ namespace ICSharpCode.SharpDevelop.Dom
get;
}
string DotNetName {
get;
}
IReturnType ReturnType {
get;
set;

2
src/Main/Base/Project/Src/Gui/Components/ExtTreeView/ExtTreeView.cs

@ -179,6 +179,8 @@ namespace ICSharpCode.SharpDevelop.Gui @@ -179,6 +179,8 @@ namespace ICSharpCode.SharpDevelop.Gui
protected override void OnBeforeExpand(TreeViewCancelEventArgs e)
{
base.OnBeforeExpand(e);
if (e.Node == null)
return;
inRefresh = true;
BeginUpdate();
try {

60
src/Main/Base/Project/Src/TextEditor/Gui/Editor/CompletionWindow/CodeCompletionData.cs

@ -1,11 +1,12 @@ @@ -1,11 +1,12 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Mike Krüger" email="mike@icsharpcode.net"/>
// <owner name="Mike Krüger" email="mike@icsharpcode.net"/>
// <version value="$version"/>
// </file>
using System;
using System.Drawing;
using System.Xml;
using System.Text;
using System.Text.RegularExpressions;
@ -28,6 +29,7 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor @@ -28,6 +29,7 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor
string documentation;
IClass c;
bool convertedDocumentation = false;
double priority;
/// <summary>
/// Gets the class this CodeCompletionData object was created for.
@ -48,6 +50,15 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor @@ -48,6 +50,15 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor
}
}
public double Priority {
get {
return priority;
}
set {
priority = value;
}
}
public int ImageIndex {
get {
return imageIndex;
@ -84,18 +95,27 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor @@ -84,18 +95,27 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor
}
}
string dotnetName;
void GetPriority(string dotnetName)
{
this.dotnetName = dotnetName;
priority = CodeCompletionDataUsageCache.GetPriority(dotnetName, true);
}
public CodeCompletionData(string s, int imageIndex)
{
ambience = AmbienceService.CurrentAmbience;
description = documentation = String.Empty;
text = s;
this.imageIndex = imageIndex;
GetPriority(s);
}
public CodeCompletionData(IClass c)
{
ambience = AmbienceService.CurrentAmbience;
// save class (for the delegate description shortcut
// save class (for the delegate description shortcut)
this.c = c;
imageIndex = ClassBrowserIconService.GetIcon(c);
ambience.ConversionFlags = ConversionFlags.None;
@ -104,6 +124,7 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor @@ -104,6 +124,7 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor
// Console.WriteLine("Convert : " + c);
description = ambience.Convert(c);
documentation = c.Documentation;
GetPriority(c.DotNetName);
}
public CodeCompletionData(IMethod method)
@ -115,6 +136,7 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor @@ -115,6 +136,7 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor
text = method.Name;
description = ambience.Convert(method);
documentation = method.Documentation;
GetPriority(method.DotNetName);
}
public CodeCompletionData(IField field)
@ -126,6 +148,7 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor @@ -126,6 +148,7 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor
text = field.Name;
description = ambience.Convert(field);
documentation = field.Documentation;
GetPriority(field.DotNetName);
}
public CodeCompletionData(IProperty property)
@ -137,6 +160,7 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor @@ -137,6 +160,7 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor
text = property.Name;
description = ambience.Convert(property);
documentation = property.Documentation;
GetPriority(property.DotNetName);
}
public CodeCompletionData(IEvent e)
@ -148,17 +172,43 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor @@ -148,17 +172,43 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor
text = e.Name;
description = ambience.Convert(e);
documentation = e.Documentation;
GetPriority(e.DotNetName);
}
public void InsertAction(TextEditorControl control)
public bool InsertAction(TextArea textArea, char ch)
{
((SharpDevelopTextAreaControl)control).ActiveTextAreaControl.TextArea.InsertString(text);
if (dotnetName != null) {
CodeCompletionDataUsageCache.IncrementUsage(dotnetName);
}
if (c != null && text.Length > c.Name.Length) {
textArea.InsertString(text.Substring(0, c.Name.Length + 1));
Point start = textArea.Caret.Position;
Point end;
int pos = text.IndexOf(',');
if (pos < 0) {
textArea.InsertString(text.Substring(c.Name.Length + 1));
end = textArea.Caret.Position;
end.X -= 1;
} else {
textArea.InsertString(text.Substring(c.Name.Length + 1, pos - c.Name.Length - 1));
end = textArea.Caret.Position;
textArea.InsertString(text.Substring(pos));
}
textArea.Caret.Position = start;
textArea.SelectionManager.SetSelection(start, end);
if (!char.IsLetterOrDigit(ch)) {
return true;
}
} else {
textArea.InsertString(text);
}
return false;
}
internal static Regex whitespace = new Regex(@"\s+");
/// <summary>
///
/// Converts the xml documentation string into a plain text string.
/// </summary>
public static string GetDocumentation(string doc)
{

191
src/Main/Base/Project/Src/TextEditor/Gui/Editor/CompletionWindow/CodeCompletionDataUsageCache.cs

@ -0,0 +1,191 @@ @@ -0,0 +1,191 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
// <version value="$version"/>
// </file>
using System;
using System.Collections.Generic;
using System.IO;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor
{
/// <summary>
/// Tracks the names of the top-used CodeCompletionData items and gives them higher
/// priority in the completion dropdown.
/// </summary>
public static class CodeCompletionDataUsageCache
{
struct UsageStruct {
public int Uses;
public int ShowCount;
public UsageStruct(int Uses, int ShowCount) {
this.Uses = Uses;
this.ShowCount = ShowCount;
}
}
static Dictionary<string, UsageStruct> dict;
// File format for stored CodeCompletionDataUsageCache
// long magic = 0x6567617355444343 (identifies file type = 'CCDUsage')
// short version = 1 (file version)
// int itemCount
// {
// string itemName
// int itemUses
// int itemShowCount
// }
const long magic = 0x6567617355444343;
const short version = 1;
/// <summary>Minimum number how often an item must be used to be saved to
/// the file. Items with less uses than this count also get a priority penalty.
/// (Because the first use would otherwise always be 100% priority)</summary>
const int MinUsesForSave = 3;
/// <summary>Minimum percentage (Uses * 100 / ShowCount) an item must have to be saved.</summary>
const int MinPercentageForSave = 2; // 2%
/// <summary>Maximum number of items to save.</summary>
const int MaxSaveItems = 10;
public static string CacheFilename {
get {
return Path.Combine(PropertyService.ConfigDirectory, "CodeCompletionUsageCache.dat");
}
}
static void LoadCache()
{
dict = new Dictionary<string, UsageStruct>();
ProjectService.SolutionClosed += delegate(object sender, EventArgs e) { SaveCache(); };
if (!File.Exists(CacheFilename))
return;
using (FileStream fs = new FileStream(CacheFilename, FileMode.Open, FileAccess.Read)) {
using (BinaryReader reader = new BinaryReader(fs)) {
if (reader.ReadInt64() != magic) {
Console.WriteLine("CodeCompletionDataUsageCache: wrong file magic");
return;
}
if (reader.ReadInt16() != version) {
Console.WriteLine("CodeCompletionDataUsageCache: unknown file version");
return;
}
int itemCount = reader.ReadInt32();
for (int i = 0; i < itemCount; i++) {
string key = reader.ReadString();
int uses = reader.ReadInt32();
int showCount = reader.ReadInt32();
dict.Add(key, new UsageStruct(uses, showCount));
}
}
}
Console.WriteLine("Loaded CodeCompletionDataUsageCache (" + dict.Count + " items)");
}
public static void SaveCache()
{
if (dict == null) {
return;
}
int count;
using (FileStream fs = new FileStream(CacheFilename, FileMode.Create, FileAccess.Write)) {
using (BinaryWriter writer = new BinaryWriter(fs)) {
count = SaveCache(writer);
}
}
Console.WriteLine("Saved CodeCompletionDataUsageCache (" + count + " of " + dict.Count + " items)");
}
static int SaveCache(BinaryWriter writer)
{
writer.Write(magic);
writer.Write(version);
if (dict.Count < MaxSaveItems) {
writer.Write(dict.Count);
foreach (KeyValuePair<string, UsageStruct> entry in dict) {
writer.Write(entry.Key);
writer.Write(entry.Value.Uses);
writer.Write(entry.Value.ShowCount);
}
return dict.Count;
} else {
List<KeyValuePair<string, UsageStruct>> saveItems = new List<KeyValuePair<string, UsageStruct>>();
foreach (KeyValuePair<string, UsageStruct> entry in dict) {
if (entry.Value.Uses > MinUsesForSave &&
entry.Value.Uses * 100 / entry.Value.ShowCount > MinPercentageForSave)
{
saveItems.Add(entry);
}
}
if (saveItems.Count > MaxSaveItems) {
saveItems.Sort(new SaveItemsComparer());
}
int count = Math.Min(MaxSaveItems, saveItems.Count);
writer.Write(count);
for (int i = 0; i < count; i++) {
KeyValuePair<string, UsageStruct> entry = saveItems[i];
writer.Write(entry.Key);
writer.Write(entry.Value.Uses);
writer.Write(entry.Value.ShowCount);
}
return count;
}
}
class SaveItemsComparer : IComparer<KeyValuePair<string, UsageStruct>>
{
public int Compare(KeyValuePair<string, UsageStruct> x, KeyValuePair<string, UsageStruct> y)
{
double a = ((double)x.Value.Uses / x.Value.ShowCount);
return a.CompareTo((double)y.Value.Uses / y.Value.ShowCount);
}
}
public static void ResetCache()
{
dict = new Dictionary<string, UsageStruct>();
try {
if (File.Exists(CacheFilename)) {
File.Delete(CacheFilename);
}
} catch (Exception ex) {
Console.WriteLine("CodeCompletionDataUsageCache.ResetCache(): " + ex.Message);
}
}
public static double GetPriority(string dotnetName, bool incrementShowCount)
{
if (dict == null) {
LoadCache();
}
UsageStruct usage;
if (!dict.TryGetValue(dotnetName, out usage))
return 0;
double priority = (double)usage.Uses / usage.ShowCount;
if (usage.Uses < MinUsesForSave)
priority *= 0.2;
if (incrementShowCount) {
usage.ShowCount += 1;
dict[dotnetName] = usage;
}
return priority;
}
public static void IncrementUsage(string dotnetName)
{
if (dict == null) {
LoadCache();
}
UsageStruct usage;
if (!dict.TryGetValue(dotnetName, out usage)) {
usage = new UsageStruct(0, 2);
}
usage.Uses += 1;
dict[dotnetName] = usage;
}
}
}

13
src/Main/Base/Project/Src/TextEditor/Gui/Editor/CompletionWindow/CommentCompletionDataProvider.cs

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Mike Krger" email="mike@icsharpcode.net"/>
// <owner name="Mike Krüger" email="mike@icsharpcode.net"/>
// <version value="$version"/>
// </file>
@ -121,9 +121,16 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor @@ -121,9 +121,16 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor
}
}
public void InsertAction(TextEditorControl control)
public double Priority {
get {
return 0;
}
}
public bool InsertAction(TextArea textArea, char ch)
{
((SharpDevelopTextAreaControl)control).ActiveTextAreaControl.TextArea.InsertString(text);
textArea.InsertString(text);
return false;
}
public CommentCompletionData(string text, string description)

11
src/Main/Base/Project/Src/TextEditor/Gui/Editor/CompletionWindow/TemplateCompletionDataProvider.cs

@ -82,9 +82,16 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor @@ -82,9 +82,16 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor
}
}
public void InsertAction(TextEditorControl control)
public double Priority {
get {
return 0;
}
}
public bool InsertAction(TextArea textArea, char ch)
{
((SharpDevelopTextAreaControl)control).InsertTemplate(template);
((SharpDevelopTextAreaControl)textArea.MotherTextEditorControl).InsertTemplate(template);
return false;
}
public TemplateCompletionData(CodeTemplate template)

4
src/Main/Base/Project/Src/TextEditor/Gui/Editor/SharpDevelopTextAreaControl.cs

@ -235,7 +235,9 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor @@ -235,7 +235,9 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor
{
string fileName = FileName;
if (codeCompletionWindow != null && !codeCompletionWindow.IsDisposed) {
codeCompletionWindow.ProcessKeyEvent(ch);
if (codeCompletionWindow.ProcessKeyEvent(ch)) {
return true;
}
}
try {

Loading…
Cancel
Save