Skip to content

Commit d34a9a2

Browse files
committed
Improve notebook explorer performance
Cache relative paths of items, as `IOneNoteItem.GetRelativePath` builds a string everytime. Cache the OneNote hierarchy, only query once while traversing.
1 parent 59f3f2b commit d34a9a2

6 files changed

Lines changed: 80 additions & 26 deletions

File tree

Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
- Update to Net 9.0
77
- Refactored code
88
- Migrate from `Odotocodot.OneNote.Linq 1.2.0` to `LinqToOneNote 2.0.0`
9+
- Improve notebook explorer performance.
910

1011

1112
## 2.1.2 - 2025-6-11

Flow.Launcher.Plugin.OneNote/Main.cs

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
using OneNoteApp = LinqToOneNote.OneNote;
1010
namespace Flow.Launcher.Plugin.OneNote
1111
{
12-
#nullable disable
12+
#nullable disable
1313
public class Main : IAsyncPlugin, IContextMenu, ISettingProvider, IDisposable
1414
{
1515
private PluginInitContext context;
@@ -18,6 +18,7 @@ public class Main : IAsyncPlugin, IContextMenu, ISettingProvider, IDisposable
1818
private SearchManager searchManager;
1919
private Settings settings;
2020
private IconProvider iconProvider;
21+
private VisibilityChanged visibilityChanged;
2122

2223
private static SemaphoreSlim semaphore;
2324

@@ -26,21 +27,19 @@ public Task InitAsync(PluginInitContext context)
2627
{
2728
this.context = context;
2829
settings = context.API.LoadSettingJsonStorage<Settings>();
29-
30+
31+
visibilityChanged = new VisibilityChanged(context);
3032
iconProvider = new IconProvider(context, settings);
3133
resultCreator = new ResultCreator(context, settings, iconProvider);
32-
searchManager = new SearchManager(context, settings, resultCreator);
33-
semaphore = new SemaphoreSlim(1,1);
34-
context.API.VisibilityChanged += OnVisibilityChanged;
35-
return Task.CompletedTask;
36-
}
34+
searchManager = new SearchManager(context, settings, resultCreator, visibilityChanged);
35+
semaphore = new SemaphoreSlim(1, 1);
3736

38-
private void OnVisibilityChanged(object _, VisibilityChangedEventArgs e)
39-
{
40-
if (context.CurrentPluginMetadata.Disabled || !e.IsVisible)
37+
visibilityChanged.Subscribe(static (isVisible) =>
4138
{
42-
Task.Run(OneNoteApp.ReleaseComObject);
43-
}
39+
if (!isVisible)
40+
Task.Run(OneNoteApp.ReleaseComObject);
41+
});
42+
return Task.CompletedTask;
4443
}
4544

4645
private static async Task OneNoteInitAsync(CancellationToken token)
@@ -70,7 +69,7 @@ public async Task<List<Result>> QueryAsync(Query query, CancellationToken token)
7069

7170
await init;
7271

73-
return searchManager.Query(query.Search);
72+
return searchManager.Query(query);
7473
}
7574

7675
public List<Result> LoadContextMenus(Result selectedResult)
@@ -85,7 +84,7 @@ public Control CreateSettingPanel()
8584

8685
public void Dispose()
8786
{
88-
context.API.VisibilityChanged -= OnVisibilityChanged;
87+
visibilityChanged.Dispose();
8988
semaphore.Dispose();
9089
OneNoteApp.ReleaseComObject();
9190
}

Flow.Launcher.Plugin.OneNote/ResultCreator.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Concurrent;
23
using System.Collections.Generic;
34
using System.Linq;
45
using System.Threading.Tasks;
@@ -16,6 +17,7 @@ public class ResultCreator
1617
private readonly PluginInitContext context;
1718
private readonly Settings settings;
1819
private readonly IconProvider iconProvider;
20+
private readonly ConcurrentDictionary<RelativePathKey, string> relativePaths = [];
1921

2022
private const string PathSeparator = " > ";
2123
private const string BulletPoint = "\u2022 ";
@@ -28,7 +30,9 @@ public ResultCreator(PluginInitContext context, Settings settings, IconProvider
2830
this.context = context;
2931
}
3032

31-
private static string GetNicePath(IOneNoteItem item, string separator = PathSeparator) => item.GetRelativePath(false, separator);
33+
private readonly record struct RelativePathKey(IOneNoteItem Item, string Separator);
34+
private string GetNicePath(IOneNoteItem item, string separator = PathSeparator)
35+
=> relativePaths.GetOrAdd(new RelativePathKey(item, separator), v => v.Item.GetRelativePath(false, v.Separator));
3236

3337
private string GetTitle(IOneNoteItem item, List<int>? highlightData)
3438
{

Flow.Launcher.Plugin.OneNote/Search/NotebookExplorer.cs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,22 @@ namespace Flow.Launcher.Plugin.OneNote.Search
1111
public class NotebookExplorer : SearchBase
1212
{
1313
private readonly TitleSearch titleSearch;
14-
public NotebookExplorer(PluginInitContext context, Settings settings, ResultCreator resultCreator, TitleSearch titleSearch)
14+
private Root? cache;
15+
private bool updateCache;
16+
public NotebookExplorer(PluginInitContext context, Settings settings, ResultCreator resultCreator, TitleSearch titleSearch, VisibilityChanged visibilityChanged)
1517
: base(context, settings, resultCreator, settings.Keywords.NotebookExplorer)
1618
{
1719
this.titleSearch = titleSearch;
20+
visibilityChanged.Subscribe(isVisible =>
21+
{
22+
if (!isVisible)
23+
{
24+
updateCache = true;
25+
}
26+
});
1827
}
1928

20-
public override List<Result> GetResults(string query)
29+
internal List<Result> GetResults(Query query)
2130
{
2231
if (!ValidateSearch(query, out string? search, out IOneNoteItem? parent, out IEnumerable<IOneNoteItem> collection))
2332
return resultCreator.InvalidQuery(false);
@@ -40,13 +49,21 @@ public override List<Result> GetResults(string query)
4049
return results;
4150
}
4251

43-
private bool ValidateSearch(string query, out string? lastSearch, out IOneNoteItem? parent, out IEnumerable<IOneNoteItem> collection)
52+
public override List<Result> GetResults(string query) => GetResults(query);
53+
54+
private bool ValidateSearch(Query query, out string? lastSearch, out IOneNoteItem? parent, out IEnumerable<IOneNoteItem> collection)
4455
{
4556
lastSearch = null;
4657
parent = null;
47-
collection = OneNoteApp.GetFullHierarchy().Notebooks;
48-
49-
string search = query[(query.IndexOf(Keywords.NotebookExplorer, StringComparison.Ordinal) + Keywords.NotebookExplorer.Length)..];
58+
if (updateCache || query.IsReQuery || cache == null)
59+
{
60+
cache = OneNoteApp.GetFullHierarchy();
61+
updateCache = false;
62+
}
63+
64+
collection = cache.Notebooks;
65+
66+
string search = query.Search[(query.Search.IndexOf(Keywords.NotebookExplorer, StringComparison.Ordinal) + Keywords.NotebookExplorer.Length)..];
5067
const string separator = Keywords.NotebookExplorerSeparator;
5168
var currIndex = search.IndexOf(separator, StringComparison.Ordinal);
5269
var prevIndex = 0;

Flow.Launcher.Plugin.OneNote/Search/SearchManager.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,21 @@ public class SearchManager
99
private readonly DefaultSearch defaultSearch;
1010
private readonly RecentPages recentPages;
1111

12-
public SearchManager(PluginInitContext context, Settings settings, ResultCreator resultCreator)
12+
public SearchManager(PluginInitContext context, Settings settings, ResultCreator resultCreator, VisibilityChanged visibilityChanged)
1313
{
1414
titleSearch = new TitleSearch(context, settings, resultCreator);
15-
notebookExplorer = new NotebookExplorer(context, settings, resultCreator, titleSearch);
15+
notebookExplorer = new NotebookExplorer(context, settings, resultCreator, titleSearch, visibilityChanged);
1616
recentPages = new RecentPages(context, settings, resultCreator);
1717
defaultSearch = new DefaultSearch(context, settings, resultCreator);
1818
}
19-
20-
public List<Result> Query(string search)
19+
20+
public List<Result> Query(Query query)
2121
{
22+
string search = query.Search;
2223
return search switch
2324
{
2425
{ } when search.StartsWithOrd(titleSearch.keyword) => titleSearch.GetResults(search),
25-
{ } when search.StartsWithOrd(notebookExplorer.keyword) => notebookExplorer.GetResults(search),
26+
{ } when search.StartsWithOrd(notebookExplorer.keyword) => notebookExplorer.GetResults(query),
2627
{ } when search.StartsWithOrd(recentPages.keyword) => recentPages.GetResults(search),
2728
_ => defaultSearch.GetResults(search!),
2829
};
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
namespace Flow.Launcher.Plugin.OneNote
2+
{
3+
public delegate void VisibilityChangedEventHandler(bool isVisible);
4+
public class VisibilityChanged
5+
{
6+
private event VisibilityChangedEventHandler? OnVisibilityChanged;
7+
private readonly PluginInitContext context;
8+
public VisibilityChanged(PluginInitContext context)
9+
{
10+
this.context = context;
11+
context.API.VisibilityChanged += OnVisibilityChangedWrap;
12+
}
13+
14+
private void OnVisibilityChangedWrap(object _, VisibilityChangedEventArgs e)
15+
{
16+
if (!context.CurrentPluginMetadata.Disabled)
17+
{
18+
OnVisibilityChanged?.Invoke(e.IsVisible);
19+
}
20+
}
21+
public void Subscribe(VisibilityChangedEventHandler action)
22+
{
23+
OnVisibilityChanged += action;
24+
}
25+
26+
public void Dispose()
27+
{
28+
context.API.VisibilityChanged -= OnVisibilityChangedWrap;
29+
OnVisibilityChanged = null;
30+
}
31+
}
32+
}

0 commit comments

Comments
 (0)