diff --git a/Changelog.md b/Changelog.md
index 4e0705e..aa11fd7 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,8 +1,15 @@
# Changelog
+## 3.0.1 - 2026-03-13
+
+- Fix crash on quick note with empty title (Updated to `LinqToOneNote-2.1.1`)
+- Fix incorrect **scope search** check
+- Cache OneNote hierarchy when applicable
+ - This brings the performance improvement for Notebook Explorer from last version to **recent pages** and **title search**
+
## 3.0.0 - 2026-03-04
-- **⚠ Now requires Flow Launcher version 2.1.0 or later.**
+- **⚠ Now requires Flow Launcher version `2.1.0` or later.**
- Refactored code
- Improved Notebook Explorer performance.
- Added copy link to clipboard context menu item. ([#32](https://github.com/Odotocodot/Flow.Launcher.Plugin.OneNote/issues/32))
diff --git a/Flow.Launcher.Plugin.OneNote/Flow.Launcher.Plugin.OneNote.csproj b/Flow.Launcher.Plugin.OneNote/Flow.Launcher.Plugin.OneNote.csproj
index 4c07142..f0f825c 100644
--- a/Flow.Launcher.Plugin.OneNote/Flow.Launcher.Plugin.OneNote.csproj
+++ b/Flow.Launcher.Plugin.OneNote/Flow.Launcher.Plugin.OneNote.csproj
@@ -30,7 +30,7 @@
-
+
diff --git a/Flow.Launcher.Plugin.OneNote/Keywords.cs b/Flow.Launcher.Plugin.OneNote/Keywords.cs
index bc06637..bbb74b5 100644
--- a/Flow.Launcher.Plugin.OneNote/Keywords.cs
+++ b/Flow.Launcher.Plugin.OneNote/Keywords.cs
@@ -26,6 +26,8 @@ public class Keyword
public int Length => Value.Length;
public static implicit operator string(Keyword keyword) => keyword.Value;
public override string ToString() => Value;
+
+ public static Keyword Empty { get; } = new ("");
}
//Needed for legacy as keywords where just saved as a string
diff --git a/Flow.Launcher.Plugin.OneNote/Main.cs b/Flow.Launcher.Plugin.OneNote/Main.cs
index a2184b8..fd13031 100644
--- a/Flow.Launcher.Plugin.OneNote/Main.cs
+++ b/Flow.Launcher.Plugin.OneNote/Main.cs
@@ -7,6 +7,7 @@
using Flow.Launcher.Plugin.OneNote.Search;
using Flow.Launcher.Plugin.OneNote.UI.Views;
using OneNoteApp = LinqToOneNote.OneNote;
+
namespace Flow.Launcher.Plugin.OneNote
{
#nullable disable
@@ -18,7 +19,6 @@ public class Main : IAsyncPlugin, IContextMenu, ISettingProvider, IDisposable
private SearchManager searchManager;
private Settings settings;
private IconProvider iconProvider;
- private VisibilityChanged visibilityChanged;
private static SemaphoreSlim semaphore;
@@ -28,17 +28,18 @@ public Task InitAsync(PluginInitContext context)
this.context = context;
settings = context.API.LoadSettingJsonStorage();
- visibilityChanged = new VisibilityChanged(context);
iconProvider = new IconProvider(context, settings);
resultCreator = new ResultCreator(context, settings, iconProvider);
- searchManager = new SearchManager(context, settings, resultCreator, visibilityChanged);
+ searchManager = new SearchManager(context, settings, resultCreator);
semaphore = new SemaphoreSlim(1, 1);
- visibilityChanged.Subscribe(static (isVisible) =>
+ context.API.VisibilityChanged += (_, args) =>
{
- if (!isVisible)
- Task.Run(OneNoteApp.ReleaseComObject);
- });
+ if (args.IsVisible)
+ return;
+ Task.Run(OneNoteApp.ReleaseComObject);
+ searchManager.RootCache.SetDirty();
+ };
return Task.CompletedTask;
}
@@ -84,7 +85,6 @@ public Control CreateSettingPanel()
public void Dispose()
{
- visibilityChanged.Dispose();
semaphore.Dispose();
OneNoteApp.ReleaseComObject();
}
diff --git a/Flow.Launcher.Plugin.OneNote/Search/DefaultSearch.cs b/Flow.Launcher.Plugin.OneNote/Search/DefaultSearch.cs
index 0862435..150042f 100644
--- a/Flow.Launcher.Plugin.OneNote/Search/DefaultSearch.cs
+++ b/Flow.Launcher.Plugin.OneNote/Search/DefaultSearch.cs
@@ -4,22 +4,20 @@
namespace Flow.Launcher.Plugin.OneNote.Search
{
- public class DefaultSearch : SearchBase
+ public class DefaultSearch(PluginInitContext context, Settings settings, ResultCreator resultCreator)
+ : SearchBase(context, settings, resultCreator, Keyword.Empty)
{
- public DefaultSearch(PluginInitContext context, Settings settings, ResultCreator resultCreator)
- : base(context, settings, resultCreator, null) { }
-
- public override List GetResults(string query)
+ public override List GetResults(Query query)
{
- if (!char.IsLetterOrDigit(query[0]))
+ string search = query.Search;
+ if (!char.IsLetterOrDigit(search[0]))
{
return resultCreator.InvalidQuery();
}
- return OneNoteApp.FindPages(query)
- .Select(pg => resultCreator.CreatePageResult(pg, query))
+ return OneNoteApp.FindPages(search)
+ .Select(pg => resultCreator.CreatePageResult(pg, search))
.ToList();
-
}
}
}
\ No newline at end of file
diff --git a/Flow.Launcher.Plugin.OneNote/Search/NotebookExplorer.cs b/Flow.Launcher.Plugin.OneNote/Search/NotebookExplorer.cs
index cdb1a26..d7e5efc 100644
--- a/Flow.Launcher.Plugin.OneNote/Search/NotebookExplorer.cs
+++ b/Flow.Launcher.Plugin.OneNote/Search/NotebookExplorer.cs
@@ -5,28 +5,12 @@
using LinqToOneNote.Abstractions;
using OneNoteApp = LinqToOneNote.OneNote;
-
namespace Flow.Launcher.Plugin.OneNote.Search
{
- public class NotebookExplorer : SearchBase
+ public class NotebookExplorer(PluginInitContext context, Settings settings, ResultCreator resultCreator, TitleSearch titleSearch, RootCache rootCache)
+ : SearchBase(context, settings, resultCreator, settings.Keywords.NotebookExplorer)
{
- private readonly TitleSearch titleSearch;
- private Root? cache;
- private bool updateCache;
- public NotebookExplorer(PluginInitContext context, Settings settings, ResultCreator resultCreator, TitleSearch titleSearch, VisibilityChanged visibilityChanged)
- : base(context, settings, resultCreator, settings.Keywords.NotebookExplorer)
- {
- this.titleSearch = titleSearch;
- visibilityChanged.Subscribe(isVisible =>
- {
- if (!isVisible)
- {
- updateCache = true;
- }
- });
- }
-
- internal List GetResults(Query query)
+ public override List GetResults(Query query)
{
if (!ValidateSearch(query, out string? search, out IOneNoteItem? parent, out IEnumerable collection))
return resultCreator.InvalidQuery(false);
@@ -49,19 +33,12 @@ internal List GetResults(Query query)
return results;
}
- public override List GetResults(string query) => GetResults(query);
-
private bool ValidateSearch(Query query, out string? lastSearch, out IOneNoteItem? parent, out IEnumerable collection)
{
lastSearch = null;
parent = null;
- if (updateCache || query.IsReQuery || cache == null)
- {
- cache = OneNoteApp.GetFullHierarchy();
- updateCache = false;
- }
- collection = cache.Notebooks;
+ collection = rootCache.Root.Notebooks;
string search = query.Search[(query.Search.IndexOf(Keywords.NotebookExplorer, StringComparison.Ordinal) + Keywords.NotebookExplorer.Length)..];
const string separator = Keywords.NotebookExplorerSeparator;
@@ -102,7 +79,7 @@ private List ScopedSearch(string query, IOneNoteItem parent)
if (!char.IsLetterOrDigit(query[Keywords.ScopedSearch.Length]))
return resultCreator.InvalidQuery();
- string currentSearch = query[Keywords.TitleSearch.Length..];
+ string currentSearch = query[Keywords.ScopedSearch.Length..];
var results = OneNoteApp.FindPages(currentSearch, parent)
.Select(pg => resultCreator.CreatePageResult(pg, currentSearch))
diff --git a/Flow.Launcher.Plugin.OneNote/Search/RecentPages.cs b/Flow.Launcher.Plugin.OneNote/Search/RecentPages.cs
index 6e6fcdf..41f7129 100644
--- a/Flow.Launcher.Plugin.OneNote/Search/RecentPages.cs
+++ b/Flow.Launcher.Plugin.OneNote/Search/RecentPages.cs
@@ -1,30 +1,27 @@
using System.Collections.Generic;
using System.Linq;
using LinqToOneNote;
-using OneNoteApp = LinqToOneNote.OneNote;
namespace Flow.Launcher.Plugin.OneNote.Search
{
- public class RecentPages : SearchBase
+ public class RecentPages(PluginInitContext context, Settings settings, ResultCreator resultCreator, RootCache rootCache)
+ : SearchBase(context, settings, resultCreator, settings.Keywords.RecentPages)
{
- public RecentPages(PluginInitContext context, Settings settings, ResultCreator resultCreator)
- : base(context, settings, resultCreator, settings.Keywords.RecentPages) { }
-
- public override List GetResults(string query)
+ public override List GetResults(Query query)
{
int count = settings.DefaultRecentsCount;
-
- if (query.Length > keyword.Length && int.TryParse(query[keyword.Length..], out int userChosenCount))
+ string search = query.Search;
+ if (search.Length > keyword.Length && int.TryParse(search[keyword.Length..], out int userChosenCount))
count = userChosenCount;
- return OneNoteApp.GetFullHierarchy()
- .Notebooks
- .GetAllPages()
- .FilterBySettings(settings)
- .OrderByDescending(pg => pg.LastModified)
- .Take(count)
- .Select(resultCreator.CreateRecentPageResult)
- .ToList();
+ return rootCache.Root
+ .Notebooks
+ .GetAllPages()
+ .FilterBySettings(settings)
+ .OrderByDescending(pg => pg.LastModified)
+ .Take(count)
+ .Select(resultCreator.CreateRecentPageResult)
+ .ToList();
}
}
}
\ No newline at end of file
diff --git a/Flow.Launcher.Plugin.OneNote/Search/RootCache.cs b/Flow.Launcher.Plugin.OneNote/Search/RootCache.cs
new file mode 100644
index 0000000..36d82dd
--- /dev/null
+++ b/Flow.Launcher.Plugin.OneNote/Search/RootCache.cs
@@ -0,0 +1,25 @@
+using LinqToOneNote;
+
+namespace Flow.Launcher.Plugin.OneNote.Search
+{
+ public class RootCache
+ {
+ private bool isDirty;
+ private Root? root;
+ public Root Root
+ {
+ get
+ {
+ if (!isDirty && root is not null)
+ return root;
+ root = LinqToOneNote.OneNote.GetFullHierarchy();
+ isDirty = false;
+ return root;
+ }
+ }
+ public void SetDirty()
+ {
+ isDirty = true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Flow.Launcher.Plugin.OneNote/Search/SearchBase.cs b/Flow.Launcher.Plugin.OneNote/Search/SearchBase.cs
index cc54ff1..12bc90f 100644
--- a/Flow.Launcher.Plugin.OneNote/Search/SearchBase.cs
+++ b/Flow.Launcher.Plugin.OneNote/Search/SearchBase.cs
@@ -2,22 +2,13 @@
namespace Flow.Launcher.Plugin.OneNote.Search
{
- public abstract class SearchBase
+ public abstract class SearchBase(PluginInitContext context, Settings settings, ResultCreator resultCreator, Keyword keyword)
{
- protected readonly PluginInitContext context;
- protected readonly Settings settings;
- protected readonly ResultCreator resultCreator;
-#nullable disable
- public readonly Keyword keyword;
-#nullable restore
- protected SearchBase(PluginInitContext context, Settings settings, ResultCreator resultCreator, Keyword? keyword)
- {
- this.context = context;
- this.settings = settings;
- this.resultCreator = resultCreator;
- this.keyword = keyword;
- }
+ protected readonly PluginInitContext context = context;
+ protected readonly Settings settings = settings;
+ protected readonly ResultCreator resultCreator = resultCreator;
+ public readonly Keyword keyword = keyword;
protected Keywords Keywords => settings.Keywords;
- public abstract List GetResults(string query);
+ public abstract List GetResults(Query query);
}
}
\ No newline at end of file
diff --git a/Flow.Launcher.Plugin.OneNote/Search/SearchManager.cs b/Flow.Launcher.Plugin.OneNote/Search/SearchManager.cs
index 6581584..ed56c56 100644
--- a/Flow.Launcher.Plugin.OneNote/Search/SearchManager.cs
+++ b/Flow.Launcher.Plugin.OneNote/Search/SearchManager.cs
@@ -8,24 +8,31 @@ public class SearchManager
private readonly NotebookExplorer notebookExplorer;
private readonly DefaultSearch defaultSearch;
private readonly RecentPages recentPages;
+ private readonly RootCache rootCache;
+ public RootCache RootCache => rootCache;
- public SearchManager(PluginInitContext context, Settings settings, ResultCreator resultCreator, VisibilityChanged visibilityChanged)
+ public SearchManager(PluginInitContext context, Settings settings, ResultCreator resultCreator)
{
- titleSearch = new TitleSearch(context, settings, resultCreator);
- notebookExplorer = new NotebookExplorer(context, settings, resultCreator, titleSearch, visibilityChanged);
- recentPages = new RecentPages(context, settings, resultCreator);
+ rootCache = new RootCache();
+ titleSearch = new TitleSearch(context, settings, resultCreator, rootCache);
+ notebookExplorer = new NotebookExplorer(context, settings, resultCreator, titleSearch, rootCache);
+ recentPages = new RecentPages(context, settings, resultCreator, rootCache);
defaultSearch = new DefaultSearch(context, settings, resultCreator);
}
public List Query(Query query)
{
+ if (query.IsReQuery)
+ {
+ rootCache.SetDirty();
+ }
string search = query.Search;
return search switch
{
- { } when search.StartsWithOrd(titleSearch.keyword) => titleSearch.GetResults(search),
+ { } when search.StartsWithOrd(titleSearch.keyword) => titleSearch.GetResults(query),
{ } when search.StartsWithOrd(notebookExplorer.keyword) => notebookExplorer.GetResults(query),
- { } when search.StartsWithOrd(recentPages.keyword) => recentPages.GetResults(search),
- _ => defaultSearch.GetResults(search!),
+ { } when search.StartsWithOrd(recentPages.keyword) => recentPages.GetResults(query),
+ _ => defaultSearch.GetResults(query),
};
}
}
diff --git a/Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs b/Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs
index 4c7101d..a6649fe 100644
--- a/Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs
+++ b/Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs
@@ -1,16 +1,13 @@
using System.Collections.Generic;
using System.Linq;
using LinqToOneNote;
-using OneNoteApp = LinqToOneNote.OneNote;
namespace Flow.Launcher.Plugin.OneNote.Search
{
- public class TitleSearch : SearchBase
+ public class TitleSearch(PluginInitContext context, Settings settings, ResultCreator resultCreator, RootCache rootCache)
+ : SearchBase(context, settings, resultCreator, settings.Keywords.TitleSearch)
{
- public TitleSearch(PluginInitContext context, Settings settings, ResultCreator resultCreator)
- : base(context, settings, resultCreator, settings.Keywords.TitleSearch) { }
-
- public override List GetResults(string query) => Filter(query, null, OneNoteApp.GetFullHierarchy().Notebooks);
+ public override List GetResults(Query query) => Filter(query.Search, null, rootCache.Root.Notebooks);
public List Filter(string query, IOneNoteItem? parent, IEnumerable collection)
{
diff --git a/Flow.Launcher.Plugin.OneNote/VisibilityChanged.cs b/Flow.Launcher.Plugin.OneNote/VisibilityChanged.cs
deleted file mode 100644
index 85c026d..0000000
--- a/Flow.Launcher.Plugin.OneNote/VisibilityChanged.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-namespace Flow.Launcher.Plugin.OneNote
-{
- public delegate void VisibilityChangedEventHandler(bool isVisible);
- public class VisibilityChanged
- {
- private event VisibilityChangedEventHandler? OnVisibilityChanged;
- private readonly PluginInitContext context;
- public VisibilityChanged(PluginInitContext context)
- {
- this.context = context;
- context.API.VisibilityChanged += OnVisibilityChangedWrap;
- }
-
- private void OnVisibilityChangedWrap(object _, VisibilityChangedEventArgs e)
- {
- if (!context.CurrentPluginMetadata.Disabled)
- {
- OnVisibilityChanged?.Invoke(e.IsVisible);
- }
- }
- public void Subscribe(VisibilityChangedEventHandler action)
- {
- OnVisibilityChanged += action;
- }
-
- public void Dispose()
- {
- context.API.VisibilityChanged -= OnVisibilityChangedWrap;
- OnVisibilityChanged = null;
- }
- }
-}
\ No newline at end of file
diff --git a/Readme.md b/Readme.md
index 2858425..47a3092 100644
--- a/Readme.md
+++ b/Readme.md
@@ -187,6 +187,9 @@ Searches for hierarchy items based on their title alone. Unlike [scoped search](
> [!NOTE]
> Can be used in conjunction with [notebook explorer](#notebook-explorer).
+> [!NOTE]
+> Does not rely on the OneNote Search API (uses Flow Launcher's internal search), thus bitwise operators do not work.
+

