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. + ![title search gif](doc/title_search_default.gif) ![title search gif](doc/title_search_notebook.gif)