Skip to content

Commit eddd738

Browse files
committed
Refactor search manager
1 parent 9da1f5f commit eddd738

7 files changed

Lines changed: 333 additions & 0 deletions

File tree

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using Odotocodot.OneNote.Linq;
4+
5+
namespace Flow.Launcher.Plugin.OneNote.Search
6+
{
7+
public class DefaultSearch : SearchBase
8+
{
9+
public override List<Result> GetResults(string query)
10+
{
11+
if (!char.IsLetterOrDigit(query[0]))
12+
{
13+
return resultCreator.InvalidQuery();
14+
}
15+
16+
return OneNoteApplication.FindPages(query)
17+
.Select(pg => resultCreator.CreatePageResult(pg, query))
18+
.ToList();
19+
20+
}
21+
}
22+
}
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using Odotocodot.OneNote.Linq;
5+
using Odotocodot.OneNote.Linq.Abstractions;
6+
7+
namespace Flow.Launcher.Plugin.OneNote.Search
8+
{
9+
public class NotebookExplorer : SearchBase
10+
{
11+
public override List<Result> GetResults(string query)
12+
{
13+
if (ValidateSearch(query, out string? search, out IOneNoteItem? parent, out IEnumerable<IOneNoteItem> collection))
14+
return resultCreator.InvalidQuery(false);
15+
16+
List<Result> results = search switch
17+
{
18+
// Empty search so show all in collection
19+
string when string.IsNullOrWhiteSpace(search) => ShowAll(parent, collection),
20+
21+
// Search by title
22+
not null when search.StartsWith(Keywords.TitleSearch) && parent is not OneNotePage
23+
=> TitleSearch.Filter(search, parent, collection, context, settings, resultCreator),
24+
25+
// Scoped search
26+
not null when search.StartsWith(Keywords.ScopedSearch) && parent is INotebookOrSectionGroup => ScopedSearch(search, parent),
27+
28+
// Default search
29+
_ => Explorer(search, parent, collection),
30+
};
31+
32+
if (parent == null)
33+
return results;
34+
35+
Result result = resultCreator.CreateOneNoteItemResult(parent, false, score: Result.MaxScore);
36+
result.Title = $"Open \"{parent.Name}\" in OneNote";
37+
result.SubTitle = search switch
38+
{
39+
not null when search.StartsWith(Keywords.TitleSearch) => $"Now searching by title in \"{parent.Name}\"",
40+
not null when search.StartsWith(Keywords.ScopedSearch) => $"Now searching all pages in \"{parent.Name}\"",
41+
_ => $"Use \'{Keywords.ScopedSearch}\' to search this item. Use \'{Keywords.TitleSearch}\' to search by title in this item",
42+
};
43+
44+
results.Add(result);
45+
return results;
46+
}
47+
48+
private bool ValidateSearch(string query, out string? lastSearch, out IOneNoteItem? parent, out IEnumerable<IOneNoteItem> collection)
49+
{
50+
lastSearch = null;
51+
parent = null;
52+
collection = OneNoteApplication.GetNotebooks();
53+
54+
string search = query[(query.IndexOf(Keywords.NotebookExplorer, StringComparison.Ordinal) + Keywords.NotebookExplorer.Length)..];
55+
56+
const string separator = Keywords.NotebookExplorerSeparator;
57+
var currIndex = search.IndexOf(separator, StringComparison.Ordinal);
58+
var prevIndex = 0;
59+
60+
while (currIndex != -1)
61+
{
62+
var itemName = search[prevIndex..currIndex];
63+
parent = collection.FirstOrDefault(item => item.Name == itemName);
64+
if (parent == null)
65+
return false;
66+
67+
collection = parent.Children;
68+
69+
prevIndex = currIndex + 1;
70+
currIndex = search.IndexOf(separator, currIndex + separator.Length, StringComparison.Ordinal);
71+
}
72+
73+
lastSearch = search[prevIndex..];
74+
return true;
75+
}
76+
77+
private List<Result> ShowAll(IOneNoteItem? parent, IEnumerable<IOneNoteItem> collection)
78+
{
79+
var results = collection.FilterBySettings(settings)
80+
.Select(item => resultCreator.CreateOneNoteItemResult(item, true))
81+
.ToList();
82+
83+
return results.Any() ? results : resultCreator.NoItemsInCollection(results, parent);
84+
}
85+
86+
private List<Result> ScopedSearch(string query, IOneNoteItem parent)
87+
{
88+
if (query.Length == Keywords.ScopedSearch.Length)
89+
return new List<Result>(0);
90+
91+
if (!char.IsLetterOrDigit(query[Keywords.ScopedSearch.Length]))
92+
return resultCreator.InvalidQuery();
93+
94+
string currentSearch = query[Keywords.TitleSearch.Length..];
95+
96+
return OneNoteApplication.FindPages(currentSearch, parent)
97+
.Select(pg => resultCreator.CreatePageResult(pg, currentSearch))
98+
.ToList();
99+
}
100+
101+
private List<Result> Explorer(string search, IOneNoteItem? parent, IEnumerable<IOneNoteItem> collection)
102+
{
103+
var results = collection.FilterBySettings(settings)
104+
.FuzzySearch(search, context)
105+
.Select(r => resultCreator.CreateOneNoteItemResult(r.item, true, r.highlightData, r.score))
106+
.ToList();
107+
108+
// If parent is a section, pages inside can have the same name
109+
if (parent is not OneNoteSection && results.Any(result => string.Equals(search.Trim(), result.Title, StringComparison.OrdinalIgnoreCase)))
110+
return results;
111+
112+
if (parent?.IsInRecycleBin() == true)
113+
return results;
114+
115+
//Add option to create new items
116+
switch (parent)
117+
{
118+
case null:
119+
results.Add(resultCreator.CreateNewNotebookResult(search));
120+
break;
121+
case INotebookOrSectionGroup:
122+
results.Add(resultCreator.CreateNewSectionResult(search, parent));
123+
results.Add(resultCreator.CreateNewSectionGroupResult(search, parent));
124+
break;
125+
case OneNoteSection section:
126+
if (!section.Locked)
127+
{
128+
results.Add(resultCreator.CreateNewPageResult(search, section));
129+
}
130+
break;
131+
}
132+
133+
return results;
134+
}
135+
}
136+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using Odotocodot.OneNote.Linq;
4+
5+
namespace Flow.Launcher.Plugin.OneNote.Search
6+
{
7+
public class RecentPages : SearchBase
8+
{
9+
public override List<Result> GetResults(string query)
10+
{
11+
int count = settings.DefaultRecentsCount;
12+
13+
if (query.Length > Keyword.Length && int.TryParse(query[Keyword.Length..], out int userChosenCount))
14+
count = userChosenCount;
15+
16+
return OneNoteApplication.GetNotebooks()
17+
.GetPages()
18+
.FilterBySettings(settings)
19+
.OrderByDescending(pg => pg.LastModified)
20+
.Take(count)
21+
.Select(resultCreator.CreateRecentPageResult)
22+
.ToList();
23+
}
24+
}
25+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace Flow.Launcher.Plugin.OneNote.Search
5+
{
6+
public abstract class SearchBase
7+
{
8+
protected readonly PluginInitContext context;
9+
protected readonly Settings settings;
10+
protected readonly ResultCreator resultCreator;
11+
public Func<string> KeywordGetter { get; init; }
12+
public string Keyword => KeywordGetter();
13+
protected Keywords Keywords => settings.Keywords;
14+
public abstract List<Result> GetResults(string query);
15+
}
16+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Flow.Launcher.Plugin.SharedModels;
4+
using Odotocodot.OneNote.Linq;
5+
6+
namespace Flow.Launcher.Plugin.OneNote.Search
7+
{
8+
public record struct SearchResult<T>(T item, List<int>? highlightData, int score) where T : IOneNoteItem;
9+
public static class SearchExtensions
10+
{
11+
public static IEnumerable<SearchResult<T>> FuzzySearch<T>(this IEnumerable<T> source, string search, PluginInitContext context) where T: IOneNoteItem
12+
{
13+
foreach (var item in source)
14+
{
15+
MatchResult match = context.API.FuzzySearch(search, item.Name);
16+
if (match.IsSearchPrecisionScoreMet())
17+
{
18+
yield return new SearchResult<T>(item, match.MatchData, match.Score);
19+
}
20+
}
21+
}
22+
public static IEnumerable<T> FilterBySettings<T>(this IEnumerable<T> source, Settings settings) where T : IOneNoteItem
23+
{
24+
foreach (var item in source)
25+
{
26+
var success = true;
27+
if (settings.ShowEncrypted && item is OneNoteSection section)
28+
{
29+
success = !section.Encrypted;
30+
}
31+
32+
if (settings.ShowRecycleBin && item.IsInRecycleBin())
33+
{
34+
success = false;
35+
}
36+
37+
if (success)
38+
{
39+
yield return item;
40+
}
41+
}
42+
}
43+
44+
public static bool StartsWithOrd(this string str, string value)
45+
{
46+
return str.StartsWith(value, StringComparison.Ordinal);
47+
}
48+
}
49+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
4+
namespace Flow.Launcher.Plugin.OneNote.Search
5+
{
6+
public class SearchManager
7+
{
8+
private readonly TitleSearch titleSearch;
9+
10+
private readonly NotebookExplorer notebookExplorer;
11+
private readonly Settings settings;
12+
13+
public SearchManager(Settings settings)
14+
{
15+
this.settings = settings;
16+
titleSearch = new TitleSearch
17+
{
18+
KeywordGetter = () => settings.Keywords.TitleSearch,
19+
};
20+
notebookExplorer = new NotebookExplorer
21+
{
22+
KeywordGetter = () => settings.Keywords.NotebookExplorer,
23+
};
24+
25+
}
26+
public List<Result> Query(Query query)
27+
{
28+
//PluginState ps;
29+
var r = query.Search switch
30+
{
31+
{ } search when search.StartsWith(titleSearch.Keyword) => titleSearch.GetResults(search),
32+
//string search when search.StartsWith(settings.Keywords.TitleSearch) => TitleSearch(ps, settings.Keywords.TitleSearch, search)
33+
34+
};
35+
return null;
36+
}
37+
}
38+
39+
public record PluginState(PluginInitContext Context, Settings Settings, ResultCreator ResultCreator)
40+
{
41+
public Keywords Keywords => Settings.Keywords;
42+
}
43+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using Odotocodot.OneNote.Linq;
4+
5+
namespace Flow.Launcher.Plugin.OneNote.Search
6+
{
7+
public class TitleSearch : SearchBase
8+
{
9+
public override List<Result> GetResults(string query)
10+
{
11+
return Filter(query, null, OneNoteApplication.GetNotebooks());
12+
}
13+
14+
public List<Result> Filter(string query, IOneNoteItem? parent, IEnumerable<IOneNoteItem> collection)
15+
{
16+
if (query.Length == Keyword.Length && parent == null)
17+
return resultCreator.SearchingByTitle();
18+
19+
var currentSearch = query[Keyword.Length..];
20+
21+
return collection.Traverse()
22+
.FilterBySettings(settings)
23+
.FuzzySearch(currentSearch, context)
24+
.Select(x => resultCreator.CreateOneNoteItemResult(x.item, false, x.highlightData, x.score))
25+
.ToList();
26+
}
27+
28+
public static List<Result> Filter(string query, IOneNoteItem? parent, IEnumerable<IOneNoteItem> collection, PluginInitContext context, Settings settings, ResultCreator resultCreator)
29+
{
30+
if (query.Length == settings.Keywords.TitleSearch.Length && parent == null)
31+
return resultCreator.SearchingByTitle();
32+
33+
var currentSearch = query[settings.Keywords.TitleSearch.Length..];
34+
35+
return collection.Traverse()
36+
.FilterBySettings(settings)
37+
.FuzzySearch(currentSearch, context)
38+
.Select(x => resultCreator.CreateOneNoteItemResult(x.item, false, x.highlightData, x.score))
39+
.ToList();
40+
}
41+
}
42+
}

0 commit comments

Comments
 (0)