Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion Changelog.md
Original file line number Diff line number Diff line change
@@ -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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
<ItemGroup>
<PackageReference Include="Flow.Launcher.Plugin" Version="5.2.0" />
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="LinqToOneNote" Version="2.1.0" />
<PackageReference Include="LinqToOneNote" Version="2.1.1" />
<PackageReference Include="System.Drawing.Common" Version="8.0.6" />
<PackageReference Include="iNKORE.UI.WPF.Modern" Version="0.10.1" />
</ItemGroup>
Expand Down
2 changes: 2 additions & 0 deletions Flow.Launcher.Plugin.OneNote/Keywords.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 8 additions & 8 deletions Flow.Launcher.Plugin.OneNote/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;

Expand All @@ -28,17 +28,18 @@ public Task InitAsync(PluginInitContext context)
this.context = context;
settings = context.API.LoadSettingJsonStorage<Settings>();

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;
}

Expand Down Expand Up @@ -84,7 +85,6 @@ public Control CreateSettingPanel()

public void Dispose()
{
visibilityChanged.Dispose();
semaphore.Dispose();
OneNoteApp.ReleaseComObject();
}
Expand Down
16 changes: 7 additions & 9 deletions Flow.Launcher.Plugin.OneNote/Search/DefaultSearch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Result> GetResults(string query)
public override List<Result> 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();

}
}
}
33 changes: 5 additions & 28 deletions Flow.Launcher.Plugin.OneNote/Search/NotebookExplorer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Result> GetResults(Query query)
public override List<Result> GetResults(Query query)
{
if (!ValidateSearch(query, out string? search, out IOneNoteItem? parent, out IEnumerable<IOneNoteItem> collection))
return resultCreator.InvalidQuery(false);
Expand All @@ -49,19 +33,12 @@ internal List<Result> GetResults(Query query)
return results;
}

public override List<Result> GetResults(string query) => GetResults(query);

private bool ValidateSearch(Query query, out string? lastSearch, out IOneNoteItem? parent, out IEnumerable<IOneNoteItem> 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;
Expand Down Expand Up @@ -102,7 +79,7 @@ private List<Result> 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))
Expand Down
29 changes: 13 additions & 16 deletions Flow.Launcher.Plugin.OneNote/Search/RecentPages.cs
Original file line number Diff line number Diff line change
@@ -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<Result> GetResults(string query)
public override List<Result> 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();
}
}
}
25 changes: 25 additions & 0 deletions Flow.Launcher.Plugin.OneNote/Search/RootCache.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
21 changes: 6 additions & 15 deletions Flow.Launcher.Plugin.OneNote/Search/SearchBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Result> GetResults(string query);
public abstract List<Result> GetResults(Query query);
}
}
21 changes: 14 additions & 7 deletions Flow.Launcher.Plugin.OneNote/Search/SearchManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Result> 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),
};
}
}
Expand Down
9 changes: 3 additions & 6 deletions Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs
Original file line number Diff line number Diff line change
@@ -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<Result> GetResults(string query) => Filter(query, null, OneNoteApp.GetFullHierarchy().Notebooks);
public override List<Result> GetResults(Query query) => Filter(query.Search, null, rootCache.Root.Notebooks);

public List<Result> Filter(string query, IOneNoteItem? parent, IEnumerable<IOneNoteItem> collection)
{
Expand Down
32 changes: 0 additions & 32 deletions Flow.Launcher.Plugin.OneNote/VisibilityChanged.cs

This file was deleted.

3 changes: 3 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
Loading