diff --git a/autogen.sh b/autogen.sh index a084fa57..a52f1f18 100755 --- a/autogen.sh +++ b/autogen.sh @@ -6,7 +6,8 @@ test -f mu/mu.cc || { exit 1 } -if test -z `which autoreconf`; then +command -V autoreconf > /dev/null +if [[ $? != 0 ]]; then echo "*** No autoreconf found, please install it ***" exit 1 fi diff --git a/lib/parser/parser.cc b/lib/parser/parser.cc index cdb6f55f..78413f1f 100644 --- a/lib/parser/parser.cc +++ b/lib/parser/parser.cc @@ -239,10 +239,13 @@ factor_2 (Mux::Tokens& tokens, Node::Type& op, ProcPtr proc, tokens.pop_front(); op = Node::Type::OpAnd; } break; + case Token::Type::Open: case Token::Type::Data: + case Token::Type::Not: op = Node::Type::OpAnd; // implicit AND break; + default: return empty(); } diff --git a/lib/parser/test-parser.cc b/lib/parser/test-parser.cc index e0e144d7..5fe5e0a0 100644 --- a/lib/parser/test-parser.cc +++ b/lib/parser/test-parser.cc @@ -84,8 +84,9 @@ test_complex () { "foo and bar or cuux", R"#((or(and(value "" "foo")(value "" "bar")))#" + std::string(R"#((value "" "cuux")))#") }, + { "a and not b", - R"#((andnot(value "" "a")(value "" "b")))#" + R"#((and(value "" "a")(not(value "" "b"))))#" }, { "a and b and c", R"#((and(value "" "a")(and(value "" "b")(value "" "c"))))#" @@ -97,7 +98,7 @@ test_complex () R"#((and(value "" "a")(value "" "b")))#" }, { "a not b", // implicit and not - R"#((andnot(value "" "a")(value "" "b")))#" + R"#((and(value "" "a")(not(value "" "b"))))#" }, { "not b", // implicit and not R"#((not(value "" "b")))#" diff --git a/lib/parser/utils.cc b/lib/parser/utils.cc index 825ca725..d79b778c 100644 --- a/lib/parser/utils.cc +++ b/lib/parser/utils.cc @@ -149,9 +149,6 @@ Mux::utf8_clean (const std::string& dirty) return clean; } - - - std::vector Mux::split (const std::string& str, const std::string& sepa) { @@ -213,7 +210,7 @@ date_boundary (bool is_first) } std::string -Mux::date_to_time_t_string (time_t t) +Mux::date_to_time_t_string (int64_t t) { char buf[sizeof(InternalDateMax)]; snprintf (buf, sizeof(buf), InternalDateFormat, t); @@ -332,7 +329,7 @@ Mux::date_to_time_t_string (const std::string& dstr, bool is_first) return date_boundary (is_first); } - t = (gint64)g_date_time_to_unix (dtime); + t = g_date_time_to_unix (dtime); g_date_time_unref (dtime); if (t < 0 || t > 9999999999) diff --git a/lib/parser/utils.hh b/lib/parser/utils.hh index 28494c15..71488de8 100644 --- a/lib/parser/utils.hh +++ b/lib/parser/utils.hh @@ -88,13 +88,14 @@ std::string format (const char *frm, ...) std::string date_to_time_t_string (const std::string& date, bool first); /** - * time_t expressed as a string with a 10-digit time_t + * 64-bit incarnation of time_t expressed as a 10-digit string. Uses 64-bit for the time-value, + * regardless of the size of time_t. * - * @param t + * @param t some time value * * @return */ -std::string date_to_time_t_string (time_t t); +std::string date_to_time_t_string (int64_t t); diff --git a/lib/tests/test-mu-str.c b/lib/tests/test-mu-str.c index 0bcb4dad..bbdb2483 100644 --- a/lib/tests/test-mu-str.c +++ b/lib/tests/test-mu-str.c @@ -122,8 +122,9 @@ test_parse_arglist (void) g_assert_cmpstr (g_hash_table_lookup (hash, "cmd"), ==, "find"); + /* note, mu4e now passes queries as base64 */ g_assert_cmpstr (g_hash_table_lookup (hash, "query"), ==, - "maildir:\"/sent items\""); + "maildir:/sent items"); g_assert_cmpstr (g_hash_table_lookup (hash, "maxnum"), ==, "500"); diff --git a/mu4e/mu4e-headers.el b/mu4e/mu4e-headers.el index 468fe5c7..77b95ad9 100644 --- a/mu4e/mu4e-headers.el +++ b/mu4e/mu4e-headers.el @@ -183,20 +183,23 @@ query have been received and are displayed." (defcustom mu4e-headers-search-bookmark-hook nil "Hook run just after we invoke a bookmarked search. This -function receives the query as its parameter. +function receives the query as its parameter, before any +rewriting as per `mu4e-query-rewrite-function' has taken place. -The reason to use this instead of `mu4e-headers-search-hook' -is if you only want to execute a hook when a search is entered -via a bookmark, e.g. if you'd like to treat the bookmarks as a -custom folder and change the options for the search, -e.g. `mu4e-headers-show-threads', `mu4e-headers-include-related', -`mu4e-headers-skip-duplicates` or `mu4e-headers-results-limit'." +The reason to use this instead of `mu4e-headers-search-hook' is +if you only want to execute a hook when a search is entered via a +bookmark, e.g. if you'd like to treat the bookmarks as a custom +folder and change the options for the search, e.g. +`mu4e-headers-show-threads', `mu4e-headers-include-related', +`mu4e-headers-skip-duplicates` or `mu4e-headers-results-limit'. +" :type 'hook :group 'mu4e-headers) (defcustom mu4e-headers-search-hook nil "Hook run just before executing a new search operation. This -function receives the query as its parameter. +function receives the query as its parameter, before any +rewriting as per `mu4e-query-rewrite-function' has taken place This is a more general hook facility than the `mu4e-headers-search-bookmark-hook'. It gets called on every @@ -1084,7 +1087,31 @@ docid is not found." (unless ignore-missing (mu4e-error "Cannot find message with docid %S" docid))))) + + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defcustom mu4e-query-rewrite-function 'identity + "Function that takes a search expression string, and returns a + possibly changed search expression string. + +This function is applied on the search expression just before +searching, and allows users to modify the query. + +For instance, we could change and of workmail into +\"maildir:/long-path-to-work-related-emails\", by setting the function + +(setq mu4e-query-rewrite-function + (lambda(expr) + (replace-regexp-in-string \"workmail\" + \"maildir:/long-path-to-work-related-emails\" expr))) + +It is good to remember that the replacement does not understand +anything about the query, it just does text replacement." + :type 'function + :group 'mu4e) + + (defun mu4e~headers-search-execute (expr ignore-history) "Search in the mu database for EXPR, and switch to the output buffer for the results. If IGNORE-HISTORY is true, do *not* update @@ -1093,7 +1120,8 @@ the query history stack." ;; `mu4e~headers-query-next' or `mu4e~headers-query-prev'. ;;(mu4e-hide-other-mu4e-buffers) (let* ((buf (get-buffer-create mu4e~headers-buffer-name)) - (inhibit-read-only t) + (inhibit-read-only t) + (rewritten-expr (funcall mu4e-query-rewrite-function expr)) (maxnum (unless mu4e-headers-full-search mu4e-headers-results-limit))) (with-current-buffer buf (mu4e-headers-mode) @@ -1103,7 +1131,7 @@ the query history stack." (mu4e~headers-push-query mu4e~headers-last-query 'past))) (setq mode-name "mu4e-headers" - mu4e~headers-last-query expr) + mu4e~headers-last-query rewritten-expr) (add-to-list 'global-mode-string '(:eval (concat @@ -1126,7 +1154,7 @@ the query history stack." (run-hook-with-args 'mu4e-headers-search-hook expr) (mu4e~headers-clear mu4e~searching) (mu4e~proc-find - expr + rewritten-expr mu4e-headers-show-threads mu4e-headers-sort-field mu4e-headers-sort-direction @@ -1387,7 +1415,9 @@ or `past'." "Whether to automatically view (open) the target message (as per `mu4e~headers-msgid-target').") -(defun mu4e-headers-search (&optional expr prompt edit ignore-history msgid show) + +(defun mu4e-headers-search (&optional expr prompt edit + ignore-history msgid show) "Search in the mu database for EXPR, and switch to the output buffer for the results. This is an interactive function which ask user for EXPR. PROMPT, if non-nil, is the prompt used by this diff --git a/mu4e/mu4e-view.el b/mu4e/mu4e-view.el index 8c53f1b3..590de03a 100644 --- a/mu4e/mu4e-view.el +++ b/mu4e/mu4e-view.el @@ -328,8 +328,8 @@ marking if it still had that." (goto-char (point-min)) (mu4e~fontify-cited) (mu4e~fontify-signature) + (mu4e~view-make-urls-clickable) (mu4e~view-show-images-maybe msg) - (mu4e~view-make-urls-clickable) (when embedded (local-set-key "q" 'kill-buffer-and-window)) (unless mode-enabled (run-mode-hooks 'mu4e-view-mode-hook))))) (switch-to-buffer buf)))