22
23
using System.Collections.Generic;
24
using System.Runtime.Serialization;
25
using System.Runtime.Serialization.Formatters.Binary;
26
30
namespace Do.Core {
28
class RelevanceProvider {
30
public static RelevanceProvider GetProvider ()
32
return new HistogramRelevanceProvider ();
32
public interface IRelevanceProvider
34
void IncreaseRelevance (Element target, string match, Element other);
35
void DecreaseRelevance (Element target, string match, Element other);
36
float GetRelevance (Element target, string match, Element other);
40
public abstract class RelevanceProvider : IRelevanceProvider {
42
const int SerializeInterval = 10 * 60 * 1000;
44
static RelevanceProvider ()
46
DefaultProvider = Deserialize () ?? new HistogramRelevanceProvider ();
47
GLib.Timeout.Add (SerializeInterval, OnSerializeTimer);
50
public static IRelevanceProvider DefaultProvider { get; private set; }
52
public static string RelevanceFile {
54
return Path.Combine (Services.Paths.UserDataDirectory, "relevance8");
58
static bool OnSerializeTimer () {
59
Gtk.Application.Invoke ((sender, args) => Serialize (DefaultProvider));
64
/// Deserializes relevance data.
66
private static IRelevanceProvider Deserialize ()
68
IRelevanceProvider provider = null;
71
using (Stream s = File.OpenRead (RelevanceFile)) {
72
BinaryFormatter f = new BinaryFormatter ();
73
provider = f.Deserialize (s) as IRelevanceProvider;
75
Log<RelevanceProvider>.Debug ("Successfully loaded learned usage data.");
76
} catch (FileNotFoundException) {
77
} catch (Exception e) {
78
Log<RelevanceProvider>.Error ("Failed to load learned usage data: {0}", e.Message);
84
/// Serializes relevance data.
86
private static void Serialize (IRelevanceProvider provider)
89
using (Stream s = File.OpenWrite (RelevanceFile)) {
90
BinaryFormatter f = new BinaryFormatter ();
91
f.Serialize (s, provider);
93
Log<RelevanceProvider>.Debug ("Successfully saved learned usage data.");
94
} catch (Exception e) {
95
Log<RelevanceProvider>.Error ("Failed to save learned usage data: {0}", e.Message);
57
string ls = s.ToLower();
121
string ls = s.ToLower ();
59
123
//Find the shortest possible substring that matches the query
60
124
//and get the ration of their lengths for a base score
61
int[] match = findBestSubstringMatchIndices(ls, query);
125
int[] match = findBestSubstringMatchIndices (ls, query);
62
126
if ((match[1] - match[0]) == 0) return 0;
63
127
score = query.Length / (float)(match[1] - match[0]);
64
128
if (score == 0) return 0;
66
130
//Now we weight by string length so shorter strings are better
67
131
score = score * .7F + query.Length / s.Length * .3F;
69
132
//Bonus points if the characters start words
70
133
float good = 0, bad = 1;
71
134
int firstCount = 0;
72
for(int i=match[0]; i<match[1]-1; i++)
135
for(int i=Math.Max (match[0]-1,0); i<match[1]-1; i++)
137
if (char.IsWhiteSpace (s[i]))
76
if(query.Contains(ls[i+1].ToString()))
139
if (query.Contains (ls[i + 1].ToString ()))
83
146
//A first character match counts extra
84
147
if(query[0] == ls[0])
87
150
//The longer the acronym, the better it scores
88
good += firstCount*firstCount*4;
90
//Better yet if the match itself started there
94
//Super bonus if the whole match is at the beginning
95
if(match[1] == (query.Length - 1))
151
good += firstCount * firstCount * 4;
98
153
//Super-duper bonus if it is a perfect match
130
185
/// A two item array containing the start and end indices of the match.
131
186
/// No match returns {-1.-1}
133
protected static int[] findBestSubstringMatchIndices(string s, string query)
188
protected static int[] findBestSubstringMatchIndices (string s, string query)
135
if(query.Length == 0)
136
return new int[] {0,0};
190
if (query.Length == 0)
191
return new int[] {0, 0};
139
int[] bestMatch = {-1,-1};
194
int[] bestMatch = {-1, -1};
141
196
//Find the last instance of the last character of the query
142
197
//since we never need to search beyond that
143
198
int lastChar = s.Length - 1;
144
while((lastChar >= 0) && (s[lastChar] != query[query.Length - 1]))
199
while (0 <= lastChar && s[lastChar] != query[query.Length - 1])
147
202
//No instance of the character?
149
204
return bestMatch;
151
206
//Loop through each instance of the first character in query
152
while ((index = s.IndexOf(query[0], index+1, lastChar-index)) >= 0) {
207
while ( 0 <= (index = s.IndexOf (query[0], index + 1, lastChar - index))) {
153
208
//Is there even room for a match?
154
if(index > lastChar + 1 - query.Length) break;
209
if (index > lastChar + 1 - query.Length) break;
156
211
//Look for the best match in the tail
157
212
//We know the first char matches, so we dont check it.
158
213
int cur = index + 1;
160
while(qcur < query.Length && cur < s.Length)
161
if(query[qcur] == s[cur++])
215
while (qcur < query.Length && cur < s.Length)
216
if (query[qcur] == s[cur++])
164
if((qcur == query.Length) && (((cur - index) < (bestMatch[1] - bestMatch[0])) || (bestMatch[0] == -1))) {
219
if (qcur == query.Length && (cur - index < bestMatch[1] - bestMatch[0] || bestMatch[0] == -1)) {
165
220
bestMatch[0] = index;
166
221
bestMatch[1] = cur;
169
if(index == s.Length - 1)
224
if (index == s.Length - 1)
173
228
return bestMatch;
176
public virtual void IncreaseRelevance (DoObject r, string match, DoObject other)
180
public virtual void DecreaseRelevance (DoObject r, string match, DoObject other)
184
public virtual float GetRelevance (DoObject r, string match, DoObject other)
186
return StringScoreForAbbreviation (r.Name, match);
189
public virtual bool CanBeFirstResultForKeypress (DoObject r, char a)
231
public virtual void IncreaseRelevance (Element r, string match, Element other)
235
public virtual void DecreaseRelevance (Element r, string match, Element other)
239
public virtual float GetRelevance (Element r, string match, Element other)
241
return StringScoreForAbbreviation (r.Safe.Name, match);