From 124b371850134205e78ad3acd19900a60459eaef Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Tue, 2 Sep 2025 21:08:54 +0300 Subject: [PATCH] labels: allow restoring cache-map E.g. with unexpected termination for mu it is possible that the labels-cache (i.e., the one that is used for auto-completion) gets disconnected from reality. Add a --restore option to `mu label list` to restore the actual labels from the labels seen in the store. --- lib/mu-store-labels.cc | 19 +++++++++++++++++++ lib/mu-store-labels.hh | 15 +++++++++++++-- lib/mu-store.cc | 8 ++++++++ lib/mu-store.hh | 12 ++++++++++++ man/mu-label.1.org | 9 +++++++++ mu/mu-cmd-label.cc | 11 ++++++++--- mu/mu-options.cc | 2 ++ mu/mu-options.hh | 4 +++- mu4e/mu4e.texi | 20 +++++++++----------- 9 files changed, 83 insertions(+), 17 deletions(-) diff --git a/lib/mu-store-labels.cc b/lib/mu-store-labels.cc index 61666aa1..8653c84c 100644 --- a/lib/mu-store-labels.cc +++ b/lib/mu-store-labels.cc @@ -23,6 +23,25 @@ using namespace Mu; +Result +Mu::LabelsCache::restore(const Store& store) +{ + const auto res{store.run_query("")}; + if (!res) + return Err(Error{Error::Code::Query, + "failed to run query: {}", + *res.error().what()}); + label_map_.clear(); + + for (auto&& item: *res) { + if (auto &&msg{item.message()}; msg) { + for (const auto& label: msg->labels()) + increase(label); + } + } + return Ok(); +} + namespace { constexpr std::string_view path_key = "path:"; constexpr std::string_view message_id_key = "message-id:"; diff --git a/lib/mu-store-labels.hh b/lib/mu-store-labels.hh index df4dde02..61049730 100644 --- a/lib/mu-store-labels.hh +++ b/lib/mu-store-labels.hh @@ -30,6 +30,8 @@ namespace Mu { +class Store; + /** * The cache keeps track of what labels are being used. This can be used * for completion etc. and `mu label list` @@ -143,6 +145,17 @@ public: return map; } + + /** + * Restore the labels-cache from the labels seen in the store. + * + * @param store a store + * + * @return Ok() or some error + */ + Result restore(const Store& store); + + /** * Is the cache "dirty"? * @@ -159,8 +172,6 @@ private: mutable bool dirty_{}; }; -class Store; - /** * Export labels to a file * diff --git a/lib/mu-store.cc b/lib/mu-store.cc index 0d4dba64..de148aff 100644 --- a/lib/mu-store.cc +++ b/lib/mu-store.cc @@ -694,6 +694,14 @@ Store::clear_labels(Message& message) return Ok(std::move(updates)); } +Result +Store::restore_label_map() +{ + std::unique_lock lock{priv_->lock_}; + + return priv_->labels_cache_.restore(*this); +} + LabelsCache::Map Store::label_map() const { diff --git a/lib/mu-store.hh b/lib/mu-store.hh index 555f2f03..9aff22c6 100644 --- a/lib/mu-store.hh +++ b/lib/mu-store.hh @@ -373,6 +373,16 @@ public: */ Result clear_labels(Message& message); + /** + * Restore label-map from store + * + * Restore the labels list in the store, i.e., restore the cached list of labels which is + * used for e.g. auto-completion in mu4e from the labels in the store. + * + * @return Ok or some error. + */ + Result restore_label_map(); + /** * Get a copy of the map of labels in use. * @@ -382,6 +392,8 @@ public: */ LabelsCache::Map label_map() const; + + /** * Prototype for the ForEachMessageFunc * diff --git a/man/mu-label.1.org b/man/mu-label.1.org index acdaff8d..0bd9aeb4 100644 --- a/man/mu-label.1.org +++ b/man/mu-label.1.org @@ -71,6 +71,15 @@ when using a shell. * LIST OPTIONS The *list* command lists all the labels that are currently in use in the store. +This is the same information used for auto-completion in *mu4e*. + +With the (global, directly after *mu) *--verbose* option, this also includes the +counts. + +** --restore + + attempt to restore the list from the labels in the store. + It is possible that the list to get outdated, *--restore* fixes this. * EXPORT OPTIONS diff --git a/mu/mu-cmd-label.cc b/mu/mu-cmd-label.cc index f0df1d1a..d2be4176 100644 --- a/mu/mu-cmd-label.cc +++ b/mu/mu-cmd-label.cc @@ -113,11 +113,16 @@ label_clear(Mu::Store& store, const Options& opts) } static Result -label_list(const Mu::Store& store, const Options& opts) +label_list(Mu::Store& store, const Options& opts) { - const auto label_map{store.label_map()}; + if (opts.label.restore) { + if (!opts.quiet) + mu_println("labels: restoring list from store..."); + if (const auto res = store.restore_label_map(); !res) + return res; + } - for (const auto& [label, n]: label_map) + for (const auto& [label, n]: store.label_map()) if (opts.verbose) mu_println("{}: {}", label, n); else diff --git a/mu/mu-options.cc b/mu/mu-options.cc index e570ca88..bacaf40a 100644 --- a/mu/mu-options.cc +++ b/mu/mu-options.cc @@ -528,6 +528,8 @@ sub_label(CLI::App& sub, Options& opts) // list [[maybe_unused]] auto list = sub.add_subcommand("list", "list labels in the store"); + list->add_flag("--restore", opts.label.restore, + "Restore the label-list from the labels in store"); add_muhome_option(*list, opts); // export diff --git a/mu/mu-options.hh b/mu/mu-options.hh index e0a51fe8..66902ff0 100644 --- a/mu/mu-options.hh +++ b/mu/mu-options.hh @@ -209,7 +209,9 @@ struct Options { bool dry_run{}; /**< Merely print the messages that would be * labeled without doing so */ StringVec delta_labels; /**< labels to add (+) or remove (-) */ - bool read_only{}; /** do not require writable store */ + bool read_only{}; /**< do not require writable store */ + + bool restore{}; /**< restore the labels list */ OptString file; /** file for import/export */ diff --git a/mu4e/mu4e.texi b/mu4e/mu4e.texi index f88fb74d..95d02873 100644 --- a/mu4e/mu4e.texi +++ b/mu4e/mu4e.texi @@ -2524,8 +2524,8 @@ field. To see them in your headers / message views, you need to add the field to the @code{mu4e-headers-fields} and @code{mu4e-view-fields}, respectively. To change the labels for some message, you specify a @emph{label expression}, -which consists of a space-separated sequence of labels, each prefixed either -with a @t{+} to add the label or @t{-} to remove it. +which consists of a space-separated sequence of labels, each prefixed with +either a @t{+} to add the label, or @t{-} to remove it. For instance, to remove the @t{boring} label and add @t{urgent} from the message at point or the messages in region, press @kbd{l} and enter: @@ -2533,16 +2533,16 @@ at point or the messages in region, press @kbd{l} and enter: +urgent -boring @end example -For @emph{clearing all labels}, you mark with @kbd{L} ('unlabel'). +For clearing @emph{all} labels, you can mark with @kbd{L} ('unlabel'). You can search for labels using the @t{label:} field. For instance, @t{label:urgent} retrieves all messages labeled @t{urgent}. -It is important to note that the labels are only stored in the database (the -message files are not changed). This means that you loose this information when -you remove the database and recrate it; it is however possible to @emph{export} -the labels and re-@emph{import} them later; see the @t{mu-label} man-page for -further details. +@emph{Important}: the labels are only stored in the database (the message files +are not changed). This means that you would @emph{loose} this information when +you remove the database and recreate it, @emph{unless} you @emph{export} the +labels before removin the database and re-@emph{import} them after re-creating +and re-indexing it; see the @t{mu-label} man-page for further details. @subsection Doing @emph{something} @@ -3185,9 +3185,7 @@ It is easy to add such actions to your configuration; for instance, to enable @emph{tagging}@footnote{@t{mu4e} does not offer tagging by default since it mutates the message files, something that @t{mu}/@t{mu4e} generally try to avoid. An alternative to tagging is @emph{labeling}, @xref{Applying and clearing -labels} - -} both in the message view and headers view, you could add: +labels}}, you could add: @lisp (add-to-list 'mu4e-headers-actions '("Tag message" . mu4e-action-retag-message))