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.
This commit is contained in:
Dirk-Jan C. Binnema
2025-09-02 21:08:54 +03:00
committed by Seth Ladygo
parent 76031d93d1
commit 124b371850
9 changed files with 83 additions and 17 deletions

View File

@ -23,6 +23,25 @@
using namespace Mu; using namespace Mu;
Result<void>
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 { namespace {
constexpr std::string_view path_key = "path:"; constexpr std::string_view path_key = "path:";
constexpr std::string_view message_id_key = "message-id:"; constexpr std::string_view message_id_key = "message-id:";

View File

@ -30,6 +30,8 @@
namespace Mu { namespace Mu {
class Store;
/** /**
* The cache keeps track of what labels are being used. This can be used * The cache keeps track of what labels are being used. This can be used
* for completion etc. and `mu label list` * for completion etc. and `mu label list`
@ -143,6 +145,17 @@ public:
return map; return map;
} }
/**
* Restore the labels-cache from the labels seen in the store.
*
* @param store a store
*
* @return Ok() or some error
*/
Result<void> restore(const Store& store);
/** /**
* Is the cache "dirty"? * Is the cache "dirty"?
* *
@ -159,8 +172,6 @@ private:
mutable bool dirty_{}; mutable bool dirty_{};
}; };
class Store;
/** /**
* Export labels to a file * Export labels to a file
* *

View File

@ -694,6 +694,14 @@ Store::clear_labels(Message& message)
return Ok(std::move(updates)); return Ok(std::move(updates));
} }
Result<void>
Store::restore_label_map()
{
std::unique_lock lock{priv_->lock_};
return priv_->labels_cache_.restore(*this);
}
LabelsCache::Map LabelsCache::Map
Store::label_map() const Store::label_map() const
{ {

View File

@ -373,6 +373,16 @@ public:
*/ */
Result<Labels::DeltaLabelVec> clear_labels(Message& message); Result<Labels::DeltaLabelVec> 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<void> restore_label_map();
/** /**
* Get a copy of the map of labels in use. * Get a copy of the map of labels in use.
* *
@ -382,6 +392,8 @@ public:
*/ */
LabelsCache::Map label_map() const; LabelsCache::Map label_map() const;
/** /**
* Prototype for the ForEachMessageFunc * Prototype for the ForEachMessageFunc
* *

View File

@ -71,6 +71,15 @@ when using a shell.
* LIST OPTIONS * LIST OPTIONS
The *list* command lists all the labels that are currently in use in the store. 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 * EXPORT OPTIONS

View File

@ -113,11 +113,16 @@ label_clear(Mu::Store& store, const Options& opts)
} }
static Result<void> static Result<void>
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) if (opts.verbose)
mu_println("{}: {}", label, n); mu_println("{}: {}", label, n);
else else

View File

@ -528,6 +528,8 @@ sub_label(CLI::App& sub, Options& opts)
// list // list
[[maybe_unused]] auto list = sub.add_subcommand("list", "list labels in the store"); [[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); add_muhome_option(*list, opts);
// export // export

View File

@ -209,7 +209,9 @@ struct Options {
bool dry_run{}; /**< Merely print the messages that would be bool dry_run{}; /**< Merely print the messages that would be
* labeled without doing so */ * labeled without doing so */
StringVec delta_labels; /**< labels to add (+) or remove (-) */ 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 */ OptString file; /** file for import/export */

View File

@ -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. the @code{mu4e-headers-fields} and @code{mu4e-view-fields}, respectively.
To change the labels for some message, you specify a @emph{label expression}, 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 which consists of a space-separated sequence of labels, each prefixed with
with a @t{+} to add the label or @t{-} to remove it. 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 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: 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 +urgent -boring
@end example @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, You can search for labels using the @t{label:} field. For instance,
@t{label:urgent} retrieves all messages labeled @t{urgent}. @t{label:urgent} retrieves all messages labeled @t{urgent}.
It is important to note that the labels are only stored in the database (the @emph{Important}: the labels are only stored in the database (the message files
message files are not changed). This means that you loose this information when are not changed). This means that you would @emph{loose} this information when
you remove the database and recrate it; it is however possible to @emph{export} you remove the database and recreate it, @emph{unless} you @emph{export} the
the labels and re-@emph{import} them later; see the @t{mu-label} man-page for labels before removin the database and re-@emph{import} them after re-creating
further details. and re-indexing it; see the @t{mu-label} man-page for further details.
@subsection Doing @emph{something} @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 @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 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 avoid. An alternative to tagging is @emph{labeling}, @xref{Applying and clearing
labels} labels}}, you could add:
} both in the message view and headers view, you could add:
@lisp @lisp
(add-to-list 'mu4e-headers-actions (add-to-list 'mu4e-headers-actions
'("Tag message" . mu4e-action-retag-message)) '("Tag message" . mu4e-action-retag-message))