2020-10-14 21:39:46 -07:00
|
|
|
;;; gnus-search.el --- Search facilities for Gnus -*- lexical-binding: t; -*-
|
|
|
|
|
2022-01-01 02:45:51 -05:00
|
|
|
;; Copyright (C) 2020-2022 Free Software Foundation, Inc.
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
;; Author: Eric Abrahamsen <eric@ericabrahamsen.net>
|
|
|
|
|
2021-02-08 09:03:27 +01:00
|
|
|
;; This file is part of GNU Emacs.
|
|
|
|
|
|
|
|
;; GNU Emacs is free software: you can redistribute it and/or modify
|
2020-10-14 21:39:46 -07:00
|
|
|
;; it under the terms of the GNU General Public License as published by
|
|
|
|
;; the Free Software Foundation, either version 3 of the License, or
|
|
|
|
;; (at your option) any later version.
|
|
|
|
|
2021-02-08 09:03:27 +01:00
|
|
|
;; GNU Emacs is distributed in the hope that it will be useful,
|
2020-10-14 21:39:46 -07:00
|
|
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
;; GNU General Public License for more details.
|
|
|
|
|
|
|
|
;; You should have received a copy of the GNU General Public License
|
2021-02-08 09:03:27 +01:00
|
|
|
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
|
|
|
|
;; This file defines a generalized search language, and search engines
|
|
|
|
;; that interface with various search programs. It is responsible for
|
|
|
|
;; parsing the user's search input, sending that query to the search
|
|
|
|
;; engines, and collecting results. Results are in the form of a
|
|
|
|
;; vector of vectors, each vector representing a found article. The
|
|
|
|
;; nnselect backend interprets that value to create a group containing
|
|
|
|
;; the search results.
|
|
|
|
|
|
|
|
;; This file was formerly known as nnir. Later, the backend parts of
|
|
|
|
;; nnir became nnselect, and only the search functionality was left
|
|
|
|
;; here.
|
|
|
|
|
|
|
|
;; See the Gnus manual for details of the search language. Tests are
|
|
|
|
;; in tests/gnus-search-test.el.
|
|
|
|
|
|
|
|
;; The search parsing routines are responsible for accepting the
|
|
|
|
;; user's search query as a string and parsing it into a sexp
|
|
|
|
;; structure. The function `gnus-search-parse-query' is the entry
|
|
|
|
;; point for that. Once the query is in sexp form, it is passed to
|
|
|
|
;; the search engines themselves, which are responsible for
|
|
|
|
;; transforming the query into a form that the external program can
|
|
|
|
;; understand, and then filtering the search results into a format
|
|
|
|
;; that nnselect can understand.
|
|
|
|
|
|
|
|
;; The general flow is:
|
|
|
|
|
|
|
|
;; 1. The user calls one of `gnus-group-make-search-group' or
|
|
|
|
;; `gnus-group-make-permanent-search-group' (or a few other entry
|
|
|
|
;; points). These functions prompt for a search query, and collect
|
|
|
|
;; the groups to search, then create an nnselect group, setting an
|
|
|
|
;; 'nnselect-specs group parameter where 'nnselect-function is
|
|
|
|
;; `gnus-search-run-query', and 'nnselect-args is the search query and
|
|
|
|
;; groups to search.
|
|
|
|
|
|
|
|
;; 2. `gnus-search-run-query' is called with 'nnselect-args. It looks
|
|
|
|
;; at the groups to search, categorizes them by server, and for each
|
|
|
|
;; server finds the search engine to use. It calls each engine's
|
|
|
|
;; `gnus-search-run-search' method with the query and groups passed as
|
|
|
|
;; arguments, and the results are collected and handed off to the
|
|
|
|
;; nnselect group.
|
|
|
|
|
|
|
|
;; For information on writing new search engines, see the Gnus manual.
|
|
|
|
|
|
|
|
;; TODO: Rewrite the query parser using syntax tables and
|
|
|
|
;; `parse-partial-sexp'.
|
|
|
|
|
|
|
|
;; TODO: Refactor IMAP search so we can move code that uses nnimap-*
|
|
|
|
;; functions out into nnimap.el.
|
|
|
|
|
|
|
|
;; TODO: Is there anything we can do about sorting results?
|
|
|
|
|
|
|
|
;; TODO: Provide for returning a result count. This would probably
|
|
|
|
;; need a completely separate top-level command, since we wouldn't be
|
|
|
|
;; creating a group at all.
|
|
|
|
|
|
|
|
;;; Code:
|
|
|
|
|
|
|
|
(require 'gnus-group)
|
|
|
|
(require 'gnus-sum)
|
|
|
|
(require 'message)
|
|
|
|
(require 'gnus-util)
|
|
|
|
(require 'eieio)
|
|
|
|
(eval-when-compile (require 'cl-lib))
|
|
|
|
(autoload 'eieio-build-class-alist "eieio-opt")
|
|
|
|
(autoload 'nnmaildir-base-name-to-article-number "nnmaildir")
|
|
|
|
|
|
|
|
(defvar gnus-inhibit-demon)
|
|
|
|
(defvar gnus-english-month-names)
|
|
|
|
|
|
|
|
;;; Internal Variables:
|
|
|
|
|
2020-11-11 10:48:37 -08:00
|
|
|
;; When Gnus servers are implemented as objects or structs, give them
|
|
|
|
;; a `search-engine' slot and get rid of this variable.
|
|
|
|
(defvar gnus-search-engine-instance-alist nil
|
|
|
|
"Mapping between servers and instantiated search engines.")
|
|
|
|
|
2020-10-14 21:39:46 -07:00
|
|
|
(defvar gnus-search-history ()
|
|
|
|
"Internal history of Gnus searches.")
|
|
|
|
|
2020-11-11 10:48:37 -08:00
|
|
|
(defun gnus-search-shutdown ()
|
|
|
|
(setq gnus-search-engine-instance-alist nil))
|
|
|
|
|
|
|
|
(gnus-add-shutdown #'gnus-search-shutdown 'gnus)
|
|
|
|
|
2021-12-04 13:41:23 -08:00
|
|
|
(define-error 'gnus-search-error "Gnus search error")
|
2020-10-14 21:39:46 -07:00
|
|
|
|
2021-12-04 13:41:23 -08:00
|
|
|
(define-error 'gnus-search-parse-error "Gnus search parsing error"
|
|
|
|
'gnus-search-error)
|
|
|
|
|
|
|
|
(define-error 'gnus-search-config-error "Gnus search configuration error"
|
|
|
|
'gnus-search-error)
|
2020-11-12 20:03:05 -08:00
|
|
|
|
2020-10-14 21:39:46 -07:00
|
|
|
;;; User Customizable Variables:
|
|
|
|
|
|
|
|
(defgroup gnus-search nil
|
|
|
|
"Search groups in Gnus with assorted search engines."
|
|
|
|
:group 'gnus)
|
|
|
|
|
|
|
|
(defcustom gnus-search-use-parsed-queries nil
|
|
|
|
"When t, use Gnus' generalized search language.
|
|
|
|
The generalized search language is a search language that can be
|
|
|
|
used across all search engines that Gnus supports. See the Gnus
|
|
|
|
manual for details.
|
|
|
|
|
|
|
|
If this option is set to nil, search queries will be passed
|
|
|
|
directly to the search engines without being parsed or
|
|
|
|
transformed."
|
|
|
|
:version "28.1"
|
2021-01-30 14:27:40 -05:00
|
|
|
:type 'boolean)
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(define-obsolete-variable-alias 'nnir-ignored-newsgroups
|
|
|
|
'gnus-search-ignored-newsgroups "28.1")
|
|
|
|
|
|
|
|
(defcustom gnus-search-ignored-newsgroups ""
|
|
|
|
"A regexp to match newsgroups in the active file that should
|
2021-09-14 08:43:18 +02:00
|
|
|
be skipped when searching."
|
2020-10-14 21:39:46 -07:00
|
|
|
:version "24.1"
|
2021-01-30 14:27:40 -05:00
|
|
|
:type 'regexp)
|
2020-10-14 21:39:46 -07:00
|
|
|
|
2020-11-04 21:13:03 -08:00
|
|
|
(make-obsolete-variable
|
|
|
|
'nnir-imap-default-search-key
|
|
|
|
"specify imap search keys, or use parsed queries." "28.1")
|
|
|
|
|
2020-10-14 21:39:46 -07:00
|
|
|
;; Engine-specific configuration options.
|
|
|
|
|
|
|
|
(defcustom gnus-search-swish++-config-file
|
|
|
|
(expand-file-name "~/Mail/swish++.conf")
|
|
|
|
"Location of Swish++ configuration file.
|
|
|
|
This variable can also be set per-server."
|
2021-01-30 14:27:40 -05:00
|
|
|
:type 'file)
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(defcustom gnus-search-swish++-program "search"
|
|
|
|
"Name of swish++ search executable.
|
|
|
|
This variable can also be set per-server."
|
2021-01-30 14:27:40 -05:00
|
|
|
:type 'string)
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(defcustom gnus-search-swish++-switches '()
|
|
|
|
"A list of strings, to be given as additional arguments to swish++.
|
|
|
|
Note that this should be a list. I.e., do NOT use the following:
|
|
|
|
(setq gnus-search-swish++-switches \"-i -w\") ; wrong
|
|
|
|
Instead, use this:
|
|
|
|
(setq gnus-search-swish++-switches \\='(\"-i\" \"-w\"))
|
|
|
|
|
|
|
|
This variable can also be set per-server."
|
2021-01-30 14:27:40 -05:00
|
|
|
:type '(repeat string))
|
2020-10-14 21:39:46 -07:00
|
|
|
|
2022-02-14 16:06:14 -08:00
|
|
|
(defcustom gnus-search-swish++-remove-prefix (expand-file-name "Mail/" "~")
|
2020-10-14 21:39:46 -07:00
|
|
|
"The prefix to remove from each file name returned by swish++
|
2022-02-14 16:06:14 -08:00
|
|
|
in order to get a group name (albeit with / instead of .).
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
This variable can also be set per-server."
|
2021-01-30 14:27:40 -05:00
|
|
|
:type 'regexp)
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(defcustom gnus-search-swish++-raw-queries-p nil
|
2021-09-14 08:43:18 +02:00
|
|
|
"If t, all Swish++ engines will only accept raw search query strings."
|
2020-10-14 21:39:46 -07:00
|
|
|
:type 'boolean
|
2021-01-30 14:27:40 -05:00
|
|
|
:version "28.1")
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(defcustom gnus-search-swish-e-config-file
|
|
|
|
(expand-file-name "~/Mail/swish-e.conf")
|
|
|
|
"Configuration file for swish-e.
|
|
|
|
This variable can also be set per-server."
|
|
|
|
:type 'file
|
2021-01-30 14:27:40 -05:00
|
|
|
:version "28.1")
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(defcustom gnus-search-swish-e-program "search"
|
|
|
|
"Name of swish-e search executable.
|
|
|
|
This variable can also be set per-server."
|
|
|
|
:type 'string
|
2021-01-30 14:27:40 -05:00
|
|
|
:version "28.1")
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(defcustom gnus-search-swish-e-switches '()
|
|
|
|
"A list of strings, to be given as additional arguments to swish-e.
|
|
|
|
Note that this should be a list. I.e., do NOT use the following:
|
|
|
|
(setq gnus-search-swish-e-switches \"-i -w\") ; wrong
|
|
|
|
Instead, use this:
|
|
|
|
(setq gnus-search-swish-e-switches \\='(\"-i\" \"-w\"))
|
|
|
|
|
|
|
|
This variable can also be set per-server."
|
|
|
|
:type '(repeat string)
|
2021-01-30 14:27:40 -05:00
|
|
|
:version "28.1")
|
2020-10-14 21:39:46 -07:00
|
|
|
|
2022-02-14 16:06:14 -08:00
|
|
|
(defcustom gnus-search-swish-e-remove-prefix (expand-file-name "Mail/" "~")
|
2020-10-14 21:39:46 -07:00
|
|
|
"The prefix to remove from each file name returned by swish-e
|
2022-02-14 16:06:14 -08:00
|
|
|
in order to get a group name (albeit with / instead of .).
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
This variable can also be set per-server."
|
|
|
|
:type 'regexp
|
2021-01-30 14:27:40 -05:00
|
|
|
:version "28.1")
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(defcustom gnus-search-swish-e-index-files '()
|
|
|
|
"A list of index files to use with this Swish-e instance.
|
|
|
|
This variable can also be set per-server."
|
|
|
|
:type '(repeat file)
|
2021-01-30 14:27:40 -05:00
|
|
|
:version "28.1")
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(defcustom gnus-search-swish-e-raw-queries-p nil
|
2021-09-14 08:43:18 +02:00
|
|
|
"If t, all Swish-e engines will only accept raw search query strings."
|
2020-10-14 21:39:46 -07:00
|
|
|
:type 'boolean
|
2021-01-30 14:27:40 -05:00
|
|
|
:version "28.1")
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
;; Namazu engine, see <URL:http://www.namazu.org/>
|
|
|
|
|
|
|
|
(defcustom gnus-search-namazu-program "namazu"
|
|
|
|
"Name of Namazu search executable.
|
|
|
|
This variable can also be set per-server."
|
|
|
|
:type 'string
|
2021-01-30 14:27:40 -05:00
|
|
|
:version "28.1")
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(defcustom gnus-search-namazu-index-directory (expand-file-name "~/Mail/namazu/")
|
|
|
|
"Index directory for Namazu.
|
|
|
|
This variable can also be set per-server."
|
|
|
|
:type 'directory
|
2021-01-30 14:27:40 -05:00
|
|
|
:version "28.1")
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(defcustom gnus-search-namazu-switches '()
|
|
|
|
"A list of strings, to be given as additional arguments to namazu.
|
|
|
|
The switches `-q', `-a', and `-s' are always used, very few other switches
|
|
|
|
make any sense in this context.
|
|
|
|
|
|
|
|
Note that this should be a list. I.e., do NOT use the following:
|
|
|
|
(setq gnus-search-namazu-switches \"-i -w\") ; wrong
|
|
|
|
Instead, use this:
|
|
|
|
(setq gnus-search-namazu-switches \\='(\"-i\" \"-w\"))
|
|
|
|
|
|
|
|
This variable can also be set per-server."
|
|
|
|
:type '(repeat string)
|
2021-01-30 14:27:40 -05:00
|
|
|
:version "28.1")
|
2020-10-14 21:39:46 -07:00
|
|
|
|
2022-02-14 16:06:14 -08:00
|
|
|
(defcustom gnus-search-namazu-remove-prefix (expand-file-name "Mail/" "~")
|
2020-10-14 21:39:46 -07:00
|
|
|
"The prefix to remove from each file name returned by Namazu
|
|
|
|
in order to get a group name (albeit with / instead of .).
|
|
|
|
|
|
|
|
For example, suppose that Namazu returns file names such as
|
|
|
|
\"/home/john/Mail/mail/misc/42\". For this example, use the following
|
|
|
|
setting: (setq gnus-search-namazu-remove-prefix \"/home/john/Mail/\")
|
|
|
|
Note the trailing slash. Removing this prefix gives \"mail/misc/42\".
|
|
|
|
Gnus knows to remove the \"/42\" and to replace \"/\" with \".\" to
|
|
|
|
arrive at the correct group name, \"mail.misc\".
|
|
|
|
|
|
|
|
This variable can also be set per-server."
|
|
|
|
:type 'directory
|
2021-01-30 14:27:40 -05:00
|
|
|
:version "28.1")
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(defcustom gnus-search-namazu-raw-queries-p nil
|
2021-09-14 08:43:18 +02:00
|
|
|
"If t, all Namazu engines will only accept raw search query strings."
|
2020-10-14 21:39:46 -07:00
|
|
|
:type 'boolean
|
2021-01-30 14:27:40 -05:00
|
|
|
:version "28.1")
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(defcustom gnus-search-notmuch-program "notmuch"
|
|
|
|
"Name of notmuch search executable.
|
|
|
|
This variable can also be set per-server."
|
|
|
|
:type '(string)
|
2021-01-30 14:27:40 -05:00
|
|
|
:version "28.1")
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(defcustom gnus-search-notmuch-config-file
|
|
|
|
(expand-file-name "~/.notmuch-config")
|
|
|
|
"Configuration file for notmuch.
|
|
|
|
This variable can also be set per-server."
|
|
|
|
:type 'file
|
2021-01-30 14:27:40 -05:00
|
|
|
:version "28.1")
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(defcustom gnus-search-notmuch-switches '()
|
|
|
|
"A list of strings, to be given as additional arguments to notmuch.
|
|
|
|
Note that this should be a list. I.e., do NOT use the following:
|
|
|
|
(setq gnus-search-notmuch-switches \"-i -w\") ; wrong
|
|
|
|
Instead, use this:
|
|
|
|
(setq gnus-search-notmuch-switches \\='(\"-i\" \"-w\"))
|
|
|
|
|
|
|
|
This variable can also be set per-server."
|
|
|
|
:type '(repeat string)
|
2021-01-30 14:27:40 -05:00
|
|
|
:version "28.1")
|
2020-10-14 21:39:46 -07:00
|
|
|
|
2022-02-14 16:06:14 -08:00
|
|
|
(defcustom gnus-search-notmuch-remove-prefix (expand-file-name "Mail/" "~")
|
2020-10-14 21:39:46 -07:00
|
|
|
"The prefix to remove from each file name returned by notmuch
|
2022-02-14 16:06:14 -08:00
|
|
|
in order to get a group name (albeit with / instead of .).
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
This variable can also be set per-server."
|
|
|
|
:type 'regexp
|
2021-01-30 14:27:40 -05:00
|
|
|
:version "28.1")
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(defcustom gnus-search-notmuch-raw-queries-p nil
|
2021-09-14 08:43:18 +02:00
|
|
|
"If t, all Notmuch engines will only accept raw search query strings."
|
2020-10-14 21:39:46 -07:00
|
|
|
:type 'boolean
|
2021-01-30 14:27:40 -05:00
|
|
|
:version "28.1")
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(defcustom gnus-search-imap-raw-queries-p nil
|
2021-09-14 08:43:18 +02:00
|
|
|
"If t, all IMAP engines will only accept raw search query strings."
|
2020-10-14 21:39:46 -07:00
|
|
|
:version "28.1"
|
2021-01-30 14:27:40 -05:00
|
|
|
:type 'boolean)
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(defcustom gnus-search-mairix-program "mairix"
|
|
|
|
"Name of mairix search executable.
|
|
|
|
This variable can also be set per-server."
|
|
|
|
:version "28.1"
|
2021-01-30 14:27:40 -05:00
|
|
|
:type 'string)
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(defcustom gnus-search-mairix-config-file
|
|
|
|
(expand-file-name "~/.mairixrc")
|
|
|
|
"Configuration file for mairix.
|
|
|
|
This variable can also be set per-server."
|
|
|
|
:version "28.1"
|
2021-01-30 14:27:40 -05:00
|
|
|
:type 'file)
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(defcustom gnus-search-mairix-switches '()
|
|
|
|
"A list of strings, to be given as additional arguments to mairix.
|
|
|
|
Note that this should be a list. I.e., do NOT use the following:
|
|
|
|
(setq gnus-search-mairix-switches \"-i -w\") ; wrong
|
|
|
|
Instead, use this:
|
|
|
|
(setq gnu-search-mairix-switches \\='(\"-i\" \"-w\"))
|
|
|
|
|
|
|
|
This variable can also be set per-server."
|
|
|
|
:version "28.1"
|
2021-01-30 14:27:40 -05:00
|
|
|
:type '(repeat string))
|
2020-10-14 21:39:46 -07:00
|
|
|
|
2022-02-14 16:06:14 -08:00
|
|
|
(defcustom gnus-search-mairix-remove-prefix (expand-file-name "Mail/" "~")
|
2020-10-14 21:39:46 -07:00
|
|
|
"The prefix to remove from each file name returned by mairix
|
2022-02-14 16:06:14 -08:00
|
|
|
in order to get a group name (albeit with / instead of .).
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
This variable can also be set per-server."
|
|
|
|
:version "28.1"
|
2021-01-30 14:27:40 -05:00
|
|
|
:type 'regexp)
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(defcustom gnus-search-mairix-raw-queries-p nil
|
2021-09-14 08:43:18 +02:00
|
|
|
"If t, all Mairix engines will only accept raw search query strings."
|
2020-10-14 21:39:46 -07:00
|
|
|
:version "28.1"
|
2021-01-30 14:27:40 -05:00
|
|
|
:type 'boolean)
|
2020-10-14 21:39:46 -07:00
|
|
|
|
2022-04-07 13:14:01 +02:00
|
|
|
(defcustom gnus-search-mu-program "mu"
|
|
|
|
"Name of the mu search executable.
|
|
|
|
This can also be set per-server."
|
|
|
|
:version "29.1"
|
|
|
|
:type 'string)
|
|
|
|
|
|
|
|
(defcustom gnus-search-mu-switches nil
|
|
|
|
"A list of strings, to be given as additional arguments to mu.
|
|
|
|
Note that this should be a list. I.e., do NOT use the following:
|
|
|
|
(setq gnus-search-mu-switches \"-u -r\")
|
|
|
|
Instead, use this:
|
|
|
|
(setq gnus-search-mu-switches \\='(\"-u\" \"-r\"))
|
|
|
|
This can also be set per-server."
|
|
|
|
:version "29.1"
|
|
|
|
:type '(repeat string))
|
|
|
|
|
|
|
|
(defcustom gnus-search-mu-remove-prefix (expand-file-name "~/Mail/")
|
|
|
|
"A prefix to remove from the mu results to get a group name.
|
|
|
|
Usually this will be set to the path to your mail directory. This
|
|
|
|
can also be set per-server."
|
|
|
|
:version "29.1"
|
|
|
|
:type 'directory)
|
|
|
|
|
|
|
|
(defcustom gnus-search-mu-config-directory (expand-file-name "~/.cache/mu")
|
|
|
|
"Configuration directory for mu.
|
|
|
|
This can also be set per-server."
|
|
|
|
:version "29.1"
|
|
|
|
:type 'file)
|
|
|
|
|
|
|
|
(defcustom gnus-search-mu-raw-queries-p nil
|
|
|
|
"If t, all mu engines will only accept raw search query strings.
|
|
|
|
This can also be set per-server."
|
|
|
|
:version "29.1"
|
|
|
|
:type 'boolean)
|
|
|
|
|
2020-10-14 21:39:46 -07:00
|
|
|
;; Options for search language parsing.
|
|
|
|
|
|
|
|
(defcustom gnus-search-expandable-keys
|
|
|
|
'("from" "subject" "to" "cc" "bcc" "body" "recipient" "date"
|
|
|
|
"mark" "before" "after" "larger" "smaller" "attachment" "text"
|
|
|
|
"since" "thread" "sender" "address" "tag" "size" "grep" "limit"
|
|
|
|
"raw" "message-id" "id")
|
|
|
|
"A list of strings representing expandable search keys.
|
|
|
|
\"Expandable\" simply means the key can be abbreviated while
|
|
|
|
typing in search queries, ie \"subject\" could be entered as
|
Fix typos
* doc/lispref/display.texi (Size of Displayed Text):
* doc/lispref/windows.texi (Buffer Display Action Functions):
* etc/NEWS:
* etc/ORG-NEWS (Org-Attach has been refactored and extended):
* lisp/battery.el (display-battery-mode, battery--upower-subsribe):
* lisp/calendar/parse-time.el:
* lisp/dired-x.el:
* lisp/emacs-lisp/chart.el (chart-sequece, chart-bar-quickie):
* lisp/emacs-lisp/eldoc.el (eldoc-echo-area-use-multiline-p)
(eldoc-documentation-strategy):
* lisp/emacs-lisp/pcase.el (pcase--split-pred, pcase--u1):
* lisp/gnus/gnus-search.el (gnus-search-expandable-keys)
(gnus-search-parse-query, gnus-search-query-return-string)
(gnus-search-imap, gnus-search-imap-search-command)
(gnus-search-transform-expression):
* lisp/gnus/nnselect.el:
* lisp/isearch.el (isearch-lazy-count-format):
* lisp/mh-e/mh-show.el (mh-show-msg):
* lisp/net/dictionary-connection.el (dictionary-connection-open):
* lisp/net/dictionary.el (dictionary-default-popup-strategy)
(dictionary, dictionary-split-string, dictionary-do-select-dictionary)
(dictionary-display-dictionarys, dictionary-search)
(dictionary-tooltip-mode):
* lisp/net/eudcb-macos-contacts.el (eudc-macos-contacts-set-server):
* lisp/net/mailcap.el (mailcap-mime-data):
* lisp/net/tramp-smb.el (tramp-smb-maybe-open-connection):
* lisp/nxml/nxml-mode.el (nxml-mode):
* lisp/progmodes/cc-engine.el:
* lisp/progmodes/cperl-mode.el (cperl-mode)
(cperl-fontify-syntaxically):
* lisp/progmodes/flymake.el (flymake-diagnostic-functions):
* lisp/progmodes/verilog-mode.el (verilog--supressed-warnings)
(verilog-preprocess):
* lisp/simple.el (self-insert-uses-region-functions):
* lisp/textmodes/bibtex.el (bibtex-copy-summary-as-kill):
* lisp/textmodes/texnfo-upd.el (texinfo-insert-master-menu-list):
* src/dispnew.c:
* src/font.c (Ffont_get):
* src/indent.c (compute_motion):
* src/process.c (init_process_emacs):
* src/w32fns.c (deliver_wm_chars):
* test/lisp/jsonrpc-tests.el (deferred-action-complex-tests):
Fix typos in documentation, comments, and internal identifiers.
2021-02-18 16:41:36 +01:00
|
|
|
\"subj\" or even \"su\", though \"s\" is ambiguous between
|
2020-10-14 21:39:46 -07:00
|
|
|
\"subject\" and \"since\".
|
|
|
|
|
|
|
|
Ambiguous abbreviations will raise an error."
|
|
|
|
:version "28.1"
|
|
|
|
:type '(repeat string))
|
|
|
|
|
|
|
|
(defcustom gnus-search-date-keys
|
|
|
|
'("date" "before" "after" "on" "senton" "sentbefore" "sentsince" "since")
|
|
|
|
"A list of keywords whose value should be parsed as a date.
|
|
|
|
See the docstring of `gnus-search-parse-query' for information on
|
|
|
|
date parsing."
|
|
|
|
:version "26.1"
|
|
|
|
:type '(repeat string))
|
|
|
|
|
|
|
|
(defcustom gnus-search-contact-tables '()
|
|
|
|
"A list of completion tables used to search for messages from contacts.
|
|
|
|
Each list element should be a table or collection suitable to be
|
|
|
|
returned by `completion-at-point-functions'. That usually means
|
|
|
|
a list of strings, a hash table, or an alist."
|
|
|
|
:version "28.1"
|
2020-11-06 13:43:53 +01:00
|
|
|
:type '(repeat sexp))
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
;;; Search language
|
|
|
|
|
|
|
|
;; This "language" was generalized from the original IMAP search query
|
|
|
|
;; parsing routine.
|
|
|
|
|
|
|
|
(defun gnus-search-parse-query (string)
|
|
|
|
"Turn STRING into an s-expression based query.
|
|
|
|
The resulting query structure is passed to the various search
|
|
|
|
backends, each of which adapts it as needed.
|
|
|
|
|
|
|
|
The search \"language\" is essentially a series of key:value
|
|
|
|
expressions. Key is most often a mail header, but there are
|
|
|
|
other keys. Value is a string, quoted if it contains spaces.
|
|
|
|
Key and value are separated by a colon, no space. Expressions
|
Fix typos
* doc/lispref/display.texi (Size of Displayed Text):
* doc/lispref/windows.texi (Buffer Display Action Functions):
* etc/NEWS:
* etc/ORG-NEWS (Org-Attach has been refactored and extended):
* lisp/battery.el (display-battery-mode, battery--upower-subsribe):
* lisp/calendar/parse-time.el:
* lisp/dired-x.el:
* lisp/emacs-lisp/chart.el (chart-sequece, chart-bar-quickie):
* lisp/emacs-lisp/eldoc.el (eldoc-echo-area-use-multiline-p)
(eldoc-documentation-strategy):
* lisp/emacs-lisp/pcase.el (pcase--split-pred, pcase--u1):
* lisp/gnus/gnus-search.el (gnus-search-expandable-keys)
(gnus-search-parse-query, gnus-search-query-return-string)
(gnus-search-imap, gnus-search-imap-search-command)
(gnus-search-transform-expression):
* lisp/gnus/nnselect.el:
* lisp/isearch.el (isearch-lazy-count-format):
* lisp/mh-e/mh-show.el (mh-show-msg):
* lisp/net/dictionary-connection.el (dictionary-connection-open):
* lisp/net/dictionary.el (dictionary-default-popup-strategy)
(dictionary, dictionary-split-string, dictionary-do-select-dictionary)
(dictionary-display-dictionarys, dictionary-search)
(dictionary-tooltip-mode):
* lisp/net/eudcb-macos-contacts.el (eudc-macos-contacts-set-server):
* lisp/net/mailcap.el (mailcap-mime-data):
* lisp/net/tramp-smb.el (tramp-smb-maybe-open-connection):
* lisp/nxml/nxml-mode.el (nxml-mode):
* lisp/progmodes/cc-engine.el:
* lisp/progmodes/cperl-mode.el (cperl-mode)
(cperl-fontify-syntaxically):
* lisp/progmodes/flymake.el (flymake-diagnostic-functions):
* lisp/progmodes/verilog-mode.el (verilog--supressed-warnings)
(verilog-preprocess):
* lisp/simple.el (self-insert-uses-region-functions):
* lisp/textmodes/bibtex.el (bibtex-copy-summary-as-kill):
* lisp/textmodes/texnfo-upd.el (texinfo-insert-master-menu-list):
* src/dispnew.c:
* src/font.c (Ffont_get):
* src/indent.c (compute_motion):
* src/process.c (init_process_emacs):
* src/w32fns.c (deliver_wm_chars):
* test/lisp/jsonrpc-tests.el (deferred-action-complex-tests):
Fix typos in documentation, comments, and internal identifiers.
2021-02-18 16:41:36 +01:00
|
|
|
are implicitly ANDed; the \"or\" keyword can be used to
|
2021-09-14 08:43:18 +02:00
|
|
|
OR. \"not\" will negate the following expression, or keys can be
|
2020-10-14 21:39:46 -07:00
|
|
|
prefixed with a \"-\". The \"near\" operator will work for
|
|
|
|
engines that understand it; other engines will convert it to
|
|
|
|
\"or\". Parenthetical groups work as expected.
|
|
|
|
|
|
|
|
A key that matches the name of a mail header will search that
|
|
|
|
header.
|
|
|
|
|
|
|
|
Search keys can be expanded with TAB during entry, or left
|
|
|
|
abbreviated so long as they remain unambiguous, ie \"f\" will
|
2021-09-14 08:43:18 +02:00
|
|
|
search the \"from\" header. \"s\" will raise an error.
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
Other keys:
|
|
|
|
|
|
|
|
\"address\" will search all sender and recipient headers.
|
|
|
|
|
|
|
|
\"recipient\" will search \"To\", \"Cc\", and \"Bcc\".
|
|
|
|
|
|
|
|
\"before\" will search messages sent before the specified
|
|
|
|
date (date specifications to come later). Date is exclusive.
|
|
|
|
|
|
|
|
\"after\" (or its synonym \"since\") will search messages sent
|
|
|
|
after the specified date. Date is inclusive.
|
|
|
|
|
|
|
|
\"mark\" will search messages that have some sort of mark.
|
|
|
|
Likely values include \"flag\", \"seen\", \"read\", \"replied\".
|
|
|
|
It's also possible to use Gnus' internal marks, ie \"mark:R\"
|
|
|
|
will be interpreted as mark:read.
|
|
|
|
|
|
|
|
\"tag\" will search tags -- right now that's translated to
|
2021-09-14 08:43:18 +02:00
|
|
|
\"keyword\" in IMAP, and left as \"tag\" for notmuch. At some
|
2020-10-14 21:39:46 -07:00
|
|
|
point this should also be used to search marks in the Gnus
|
|
|
|
registry.
|
|
|
|
|
|
|
|
Other keys can be specified, provided that the search backends
|
|
|
|
know how to interpret them.
|
|
|
|
|
|
|
|
External contact-management packages can push completion tables
|
|
|
|
onto the list variable `gnus-search-contact-tables', to provide
|
|
|
|
auto-completion of contact names and addresses for keys like
|
|
|
|
\"from\" and \"to\".
|
|
|
|
|
|
|
|
Date values (any key in `gnus-search-date-keys') can be provided
|
|
|
|
in any format that `parse-time-string' can parse (note that this
|
|
|
|
can produce weird results). Dates with missing bits will be
|
2021-07-11 11:55:31 +01:00
|
|
|
interpreted as the most recent occurrence thereof (i.e. \"march
|
|
|
|
03\" is the most recent March 3rd). Lastly, relative
|
|
|
|
specifications such as 1d (one day ago) are understood. This
|
|
|
|
also accepts w, m, and y. m is assumed to be 30 days.
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
This function will accept pretty much anything as input. Its
|
|
|
|
only job is to parse the query into a sexp, and pass that on --
|
|
|
|
it is the job of the search backends to make sense of the
|
|
|
|
structured query. Malformed, unusable or invalid queries will
|
|
|
|
typically be silently ignored."
|
|
|
|
(with-temp-buffer
|
|
|
|
;; Set up the parsing environment.
|
|
|
|
(insert string)
|
|
|
|
(goto-char (point-min))
|
|
|
|
;; Now, collect the output terms and return them.
|
|
|
|
(let (out)
|
|
|
|
(while (not (gnus-search-query-end-of-input))
|
|
|
|
(push (gnus-search-query-next-expr) out))
|
|
|
|
(reverse out))))
|
|
|
|
|
|
|
|
(defun gnus-search-query-next-expr (&optional count halt)
|
|
|
|
"Return the next expression from the current buffer."
|
|
|
|
(let ((term (gnus-search-query-next-term count))
|
|
|
|
(next (gnus-search-query-peek-symbol)))
|
|
|
|
;; Deal with top-level expressions. And, or, not, near... What
|
|
|
|
;; else? Notmuch also provides xor and adj. It also provides a
|
|
|
|
;; "nearness" parameter for near and adj.
|
|
|
|
(cond
|
|
|
|
;; Handle 'expr or expr'
|
|
|
|
((and (eq next 'or)
|
|
|
|
(null halt))
|
|
|
|
(list 'or term (gnus-search-query-next-expr 2)))
|
|
|
|
;; Handle 'near operator.
|
|
|
|
((eq next 'near)
|
|
|
|
(let ((near-next (gnus-search-query-next-expr 2)))
|
|
|
|
(if (and (stringp term)
|
|
|
|
(stringp near-next))
|
|
|
|
(list 'near term near-next)
|
|
|
|
(signal 'gnus-search-parse-error
|
|
|
|
(list "\"Near\" keyword must appear between two plain strings.")))))
|
|
|
|
;; Anything else
|
|
|
|
(t term))))
|
|
|
|
|
|
|
|
(defun gnus-search-query-next-term (&optional count)
|
|
|
|
"Return the next TERM from the current buffer."
|
|
|
|
(let ((term (gnus-search-query-next-symbol count)))
|
|
|
|
;; What sort of term is this?
|
|
|
|
(cond
|
|
|
|
;; negated term
|
|
|
|
((eq term 'not) (list 'not (gnus-search-query-next-expr nil 'halt)))
|
|
|
|
;; generic term
|
|
|
|
(t term))))
|
|
|
|
|
|
|
|
(defun gnus-search-query-peek-symbol ()
|
|
|
|
"Return the next symbol from the current buffer, but don't consume it."
|
|
|
|
(save-excursion
|
|
|
|
(gnus-search-query-next-symbol)))
|
|
|
|
|
|
|
|
(defun gnus-search-query-next-symbol (&optional count)
|
|
|
|
"Return the next symbol from the current buffer, or nil if we are
|
|
|
|
at the end of the buffer. If supplied COUNT skips some symbols before
|
|
|
|
returning the one at the supplied position."
|
|
|
|
(when (and (numberp count) (> count 1))
|
|
|
|
(gnus-search-query-next-symbol (1- count)))
|
|
|
|
(let ((case-fold-search t))
|
|
|
|
;; end of input stream?
|
|
|
|
(unless (gnus-search-query-end-of-input)
|
|
|
|
;; No, return the next symbol from the stream.
|
|
|
|
(cond
|
|
|
|
;; Negated expression -- return it and advance one char.
|
|
|
|
((looking-at "-") (forward-char 1) 'not)
|
|
|
|
;; List expression -- we parse the content and return this as a list.
|
|
|
|
((looking-at "(")
|
|
|
|
(gnus-search-parse-query (gnus-search-query-return-string ")" t)))
|
|
|
|
;; Keyword input -- return a symbol version.
|
|
|
|
((looking-at "\\band\\b") (forward-char 3) 'and)
|
|
|
|
((looking-at "\\bor\\b") (forward-char 2) 'or)
|
|
|
|
((looking-at "\\bnot\\b") (forward-char 3) 'not)
|
|
|
|
((looking-at "\\bnear\\b") (forward-char 4) 'near)
|
|
|
|
;; Plain string, no keyword
|
|
|
|
((looking-at "[\"/]?\\b[^:]+\\([[:blank:]]\\|\\'\\)")
|
|
|
|
(gnus-search-query-return-string
|
|
|
|
(when (looking-at-p "[\"/]") t)))
|
|
|
|
;; Assume a K:V expression.
|
|
|
|
(t (let ((key (gnus-search-query-expand-key
|
|
|
|
(buffer-substring
|
|
|
|
(point)
|
|
|
|
(progn
|
2022-08-23 04:54:57 +02:00
|
|
|
(re-search-forward ":" (line-end-position) t)
|
2020-10-14 21:39:46 -07:00
|
|
|
(1- (point))))))
|
|
|
|
(value (gnus-search-query-return-string
|
|
|
|
(when (looking-at-p "[\"/]") t))))
|
|
|
|
(gnus-search-query-parse-kv key value)))))))
|
|
|
|
|
|
|
|
(defun gnus-search-query-parse-kv (key value)
|
|
|
|
"Handle KEY and VALUE, parsing and expanding as necessary.
|
|
|
|
This may result in (key value) being turned into a larger query
|
|
|
|
structure.
|
|
|
|
|
|
|
|
In the simplest case, they are simply consed together. String
|
|
|
|
KEY is converted to a symbol."
|
2021-03-11 01:14:30 -05:00
|
|
|
(let () ;; return
|
2020-10-14 21:39:46 -07:00
|
|
|
(cond
|
|
|
|
((member key gnus-search-date-keys)
|
|
|
|
(when (string= "after" key)
|
|
|
|
(setq key "since"))
|
|
|
|
(setq value (gnus-search-query-parse-date value)))
|
|
|
|
((equal key "mark")
|
|
|
|
(setq value (gnus-search-query-parse-mark value)))
|
|
|
|
((string= "message-id" key)
|
|
|
|
(setq key "id")))
|
2021-03-11 01:14:30 -05:00
|
|
|
(or nil ;; return
|
2020-10-14 21:39:46 -07:00
|
|
|
(cons (intern key) value))))
|
|
|
|
|
|
|
|
(defun gnus-search-query-parse-date (value &optional rel-date)
|
|
|
|
"Interpret VALUE as a date specification.
|
|
|
|
See the docstring of `gnus-search-parse-query' for details.
|
|
|
|
|
|
|
|
The result is a list of (dd mm yyyy); individual elements can be
|
|
|
|
nil.
|
|
|
|
|
|
|
|
If VALUE is a relative time, interpret it as relative to
|
|
|
|
REL-DATE, or (current-time) if REL-DATE is nil."
|
|
|
|
;; Time parsing doesn't seem to work with slashes.
|
Use string-replace instead of replace-regexp-in-string
`string-replace` is easier to understand, less error-prone, much
faster, and results in shorter Lisp and byte code. Use it where
applicable and obviously safe (erring on the conservative side).
* admin/authors.el (authors-scan-change-log):
* lisp/autoinsert.el (auto-insert-alist):
* lisp/calc/calc-prog.el (calc-edit-macro-combine-alg-ent)
(calc-edit-macro-combine-ext-command)
(calc-edit-macro-combine-var-name):
* lisp/calc/calc-units.el (math-make-unit-string):
* lisp/calendar/cal-html.el (cal-html-comment):
* lisp/calendar/cal-tex.el (cal-tex-comment):
* lisp/calendar/icalendar.el (icalendar--convert-string-for-export)
(icalendar--convert-string-for-import):
* lisp/calendar/iso8601.el (iso8601--concat-regexps)
(iso8601--full-time-match, iso8601--combined-match):
* lisp/calendar/time-date.el (format-seconds):
* lisp/calendar/todo-mode.el (todo-filter-items-filename):
* lisp/cedet/cedet-files.el (cedet-directory-name-to-file-name)
(cedet-file-name-to-directory-name):
* lisp/comint.el (comint-watch-for-password-prompt):
* lisp/dired-aux.el (dired-do-chmod):
* lisp/dired-x.el (dired-man):
* lisp/dired.el (dired-insert-directory, dired-goto-file-1):
* lisp/emacs-lisp/comp.el (comp-c-func-name):
* lisp/emacs-lisp/re-builder.el (reb-copy):
* lisp/erc/erc-dcc.el (erc-dcc-unquote-filename):
* lisp/erc/erc.el (erc-quit-reason-zippy, erc-part-reason-zippy)
(erc-update-mode-line-buffer, erc-message-english-PART):
* lisp/files.el (make-backup-file-name-1, files--transform-file-name)
(read-file-modes):
* lisp/fringe.el (fringe-mode):
* lisp/gnus/gnus-art.el (gnus-button-handle-info-url):
* lisp/gnus/gnus-group.el (gnus-group-completing-read):
* lisp/gnus/gnus-icalendar.el (gnus-icalendar-event-from-ical):
* lisp/gnus/gnus-mlspl.el (gnus-group-split-fancy):
* lisp/gnus/gnus-search.el (gnus-search-query-parse-date)
(gnus-search-transform-expression, gnus-search-run-search):
* lisp/gnus/gnus-start.el (gnus-dribble-enter):
* lisp/gnus/gnus-sum.el (gnus-summary-refer-article):
* lisp/gnus/gnus-util.el (gnus-mode-string-quote):
* lisp/gnus/message.el (message-put-addresses-in-ecomplete)
(message-parse-mailto-url, message-mailto-1):
* lisp/gnus/mml-sec.el (mml-secure-epg-sign):
* lisp/gnus/mml-smime.el (mml-smime-epg-verify):
* lisp/gnus/mml2015.el (mml2015-epg-verify):
* lisp/gnus/nnmaildir.el (nnmaildir--system-name)
(nnmaildir-request-list, nnmaildir-retrieve-groups)
(nnmaildir-request-group, nnmaildir-retrieve-headers):
* lisp/gnus/nnrss.el (nnrss-node-text):
* lisp/gnus/spam-report.el (spam-report-gmane-internal)
(spam-report-user-mail-address):
* lisp/ibuffer.el (name):
* lisp/image-dired.el (image-dired-pngnq-thumb)
(image-dired-pngcrush-thumb, image-dired-optipng-thumb)
(image-dired-create-thumb-1):
* lisp/info.el (Info-set-mode-line):
* lisp/international/mule-cmds.el (describe-language-environment):
* lisp/mail/rfc2231.el (rfc2231-parse-string):
* lisp/mail/rfc2368.el (rfc2368-parse-mailto-url):
* lisp/mail/rmail.el (rmail-insert-inbox-text)
(rmail-simplified-subject-regexp):
* lisp/mail/rmailout.el (rmail-output-body-to-file):
* lisp/mail/undigest.el (rmail-digest-rfc1153):
* lisp/man.el (Man-default-man-entry):
* lisp/mouse.el (minor-mode-menu-from-indicator):
* lisp/mpc.el (mpc--debug):
* lisp/net/browse-url.el (browse-url-mail):
* lisp/net/eww.el (eww-update-header-line-format):
* lisp/net/newst-backend.el (newsticker-save-item):
* lisp/net/rcirc.el (rcirc-sentinel):
* lisp/net/soap-client.el (soap-decode-date-time):
* lisp/nxml/rng-cmpct.el (rng-c-literal-2-re):
* lisp/nxml/xmltok.el (let*):
* lisp/obsolete/nnir.el (nnir-run-swish-e, nnir-run-hyrex)
(nnir-run-find-grep):
* lisp/play/dunnet.el (dun-doassign):
* lisp/play/handwrite.el (handwrite):
* lisp/proced.el (proced-format-args):
* lisp/profiler.el (profiler-report-header-line-format):
* lisp/progmodes/gdb-mi.el (gdb-mi-quote):
* lisp/progmodes/make-mode.el (makefile-bsdmake-rule-action-regex)
(makefile-make-font-lock-keywords):
* lisp/progmodes/prolog.el (prolog-guess-fill-prefix):
* lisp/progmodes/ruby-mode.el (ruby-toggle-string-quotes):
* lisp/progmodes/sql.el (sql-remove-tabs-filter, sql-str-literal):
* lisp/progmodes/which-func.el (which-func-current):
* lisp/replace.el (query-replace-read-from)
(occur-engine, replace-quote):
* lisp/select.el (xselect--encode-string):
* lisp/ses.el (ses-export-tab):
* lisp/subr.el (shell-quote-argument):
* lisp/term/pc-win.el (msdos-show-help):
* lisp/term/w32-win.el (w32--set-selection):
* lisp/term/xterm.el (gui-backend-set-selection):
* lisp/textmodes/picture.el (picture-tab-search):
* lisp/thumbs.el (thumbs-call-setroot-command):
* lisp/tooltip.el (tooltip-show-help-non-mode):
* lisp/transient.el (transient-format-key):
* lisp/url/url-mailto.el (url-mailto):
* lisp/vc/log-edit.el (log-edit-changelog-ours-p):
* lisp/vc/vc-bzr.el (vc-bzr-status):
* lisp/vc/vc-hg.el (vc-hg--glob-to-pcre):
* lisp/vc/vc-svn.el (vc-svn-after-dir-status):
* lisp/xdg.el (xdg-desktop-strings):
* test/lisp/electric-tests.el (defun):
* test/lisp/term-tests.el (term-simple-lines):
* test/lisp/time-stamp-tests.el (formatz-mod-del-colons):
* test/lisp/wdired-tests.el (wdired-test-bug32173-01)
(wdired-test-unfinished-edit-01):
* test/src/json-tests.el (json-parse-with-custom-null-and-false-objects):
Use `string-replace` instead of `replace-regexp-in-string`.
2021-08-08 18:58:46 +02:00
|
|
|
(let ((value (string-replace "/" "-" value))
|
2020-10-14 21:39:46 -07:00
|
|
|
(now (append '(0 0 0)
|
2021-12-05 18:25:46 -08:00
|
|
|
(seq-subseq (decode-time rel-date) 3))))
|
2020-10-14 21:39:46 -07:00
|
|
|
;; Check for relative time parsing.
|
|
|
|
(if (string-match "\\([[:digit:]]+\\)\\([dwmy]\\)" value)
|
|
|
|
(seq-subseq
|
|
|
|
(decode-time
|
|
|
|
(time-subtract
|
2021-12-16 09:40:21 -08:00
|
|
|
(encode-time now)
|
2020-10-14 21:39:46 -07:00
|
|
|
(days-to-time
|
|
|
|
(* (string-to-number (match-string 1 value))
|
|
|
|
(cdr (assoc (match-string 2 value)
|
|
|
|
'(("d" . 1)
|
|
|
|
("w" . 7)
|
|
|
|
("m" . 30)
|
|
|
|
("y" . 365))))))))
|
|
|
|
3 6)
|
|
|
|
;; Otherwise check the value of `parse-time-string'.
|
|
|
|
|
|
|
|
;; (SEC MIN HOUR DAY MON YEAR DOW DST TZ)
|
|
|
|
(let ((d-time (parse-time-string value)))
|
|
|
|
;; Did parsing produce anything at all?
|
|
|
|
(if (seq-some #'integerp (seq-subseq d-time 3 7))
|
|
|
|
(seq-subseq
|
|
|
|
;; If DOW is given, handle that specially.
|
|
|
|
(if (and (seq-elt d-time 6) (null (seq-elt d-time 3)))
|
|
|
|
(decode-time
|
2021-12-16 09:40:21 -08:00
|
|
|
(time-subtract (encode-time now)
|
2020-10-14 21:39:46 -07:00
|
|
|
(days-to-time
|
|
|
|
(+ (if (> (seq-elt d-time 6)
|
|
|
|
(seq-elt now 6))
|
|
|
|
7 0)
|
|
|
|
(- (seq-elt now 6) (seq-elt d-time 6))))))
|
|
|
|
d-time)
|
|
|
|
3 6)
|
|
|
|
;; `parse-time-string' failed to produce anything, just
|
|
|
|
;; return the string.
|
|
|
|
value)))))
|
|
|
|
|
|
|
|
(defun gnus-search-query-parse-mark (mark)
|
|
|
|
"Possibly transform MARK.
|
|
|
|
If MARK is a single character, assume it is one of the
|
|
|
|
gnus-*-mark marks, and return an appropriate string."
|
|
|
|
(if (= 1 (length mark))
|
|
|
|
(let ((m (aref mark 0)))
|
|
|
|
;; Neither pcase nor cl-case will work here.
|
|
|
|
(cond
|
|
|
|
((eql m gnus-ticked-mark) "flag")
|
|
|
|
((eql m gnus-read-mark) "read")
|
|
|
|
((eql m gnus-replied-mark) "replied")
|
|
|
|
((eql m gnus-recent-mark) "recent")
|
|
|
|
(t mark)))
|
|
|
|
mark))
|
|
|
|
|
|
|
|
(defun gnus-search-query-expand-key (key)
|
2021-07-11 09:00:33 -07:00
|
|
|
"Attempt to expand KEY to a full keyword.
|
|
|
|
Use `gnus-search-expandable-keys' as a completion table; return
|
|
|
|
KEY directly if it can't be completed. Raise an error if KEY is
|
|
|
|
ambiguous, meaning that it is a prefix of multiple known
|
|
|
|
keywords. This means that it's not possible to enter a custom
|
|
|
|
keyword that happens to be a prefix of a known keyword."
|
2021-07-10 10:00:32 -07:00
|
|
|
(let ((comp (try-completion key gnus-search-expandable-keys)))
|
|
|
|
(if (or (eql comp 't) ; Already a key.
|
|
|
|
(null comp)) ; An unknown key.
|
|
|
|
key
|
2021-07-11 09:00:33 -07:00
|
|
|
(if (null (member comp gnus-search-expandable-keys))
|
|
|
|
;; KEY is a prefix of multiple known keywords, and could not
|
|
|
|
;; be completed to something unique.
|
2021-07-10 10:00:32 -07:00
|
|
|
(signal 'gnus-search-parse-error
|
|
|
|
(list (format "Ambiguous keyword: %s" key)))
|
|
|
|
;; We completed to a unique known key.
|
|
|
|
comp))))
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(defun gnus-search-query-return-string (&optional delimited trim)
|
|
|
|
"Return a string from the current buffer.
|
|
|
|
If DELIMITED is non-nil, assume the next character is a delimiter
|
|
|
|
character, and return everything between point and the next
|
2021-07-11 11:55:31 +01:00
|
|
|
occurrence of the delimiter, including the delimiters themselves.
|
|
|
|
If TRIM is non-nil, do not return the delimiters. Otherwise,
|
2020-10-14 21:39:46 -07:00
|
|
|
return one word."
|
|
|
|
;; This function cannot handle nested delimiters, as it's not a
|
|
|
|
;; proper parser. Ie, you cannot parse "to:bob or (from:bob or
|
|
|
|
;; (cc:bob or bcc:bob))".
|
|
|
|
(let ((start (point))
|
|
|
|
(delimiter (if (stringp delimited)
|
|
|
|
delimited
|
|
|
|
(when delimited
|
|
|
|
(char-to-string (char-after)))))
|
|
|
|
end)
|
|
|
|
(if delimiter
|
|
|
|
(progn
|
|
|
|
(when trim
|
|
|
|
;; Skip past first delimiter if we're trimming.
|
|
|
|
(forward-char 1))
|
|
|
|
(while (not end)
|
|
|
|
(unless (search-forward delimiter nil t (unless trim 2))
|
|
|
|
(signal 'gnus-search-parse-error
|
|
|
|
(list (format "Unmatched delimited input with %s in query" delimiter))))
|
|
|
|
(let ((here (point)))
|
|
|
|
(unless (equal (buffer-substring (- here 2) (- here 1)) "\\")
|
|
|
|
(setq end (if trim (1- (point)) (point))
|
|
|
|
start (if trim (1+ start) start))))))
|
|
|
|
(setq end (progn (re-search-forward "\\([[:blank:]]+\\|$\\)" (point-max) t)
|
|
|
|
(match-beginning 0))))
|
|
|
|
(buffer-substring-no-properties start end)))
|
|
|
|
|
|
|
|
(defun gnus-search-query-end-of-input ()
|
|
|
|
"Are we at the end of input?"
|
2020-11-06 23:02:29 +01:00
|
|
|
(skip-chars-forward "[:blank:]")
|
2020-10-14 21:39:46 -07:00
|
|
|
(looking-at "$"))
|
|
|
|
|
|
|
|
;;; Search engines
|
|
|
|
|
|
|
|
;; Search engines are implemented as classes. This is good for two
|
|
|
|
;; things: encapsulating things like indexes and search prefixes, and
|
|
|
|
;; transforming search queries.
|
|
|
|
|
|
|
|
(defclass gnus-search-engine ()
|
|
|
|
((raw-queries-p
|
|
|
|
:initarg :raw-queries-p
|
|
|
|
:initform nil
|
|
|
|
:type boolean
|
|
|
|
:custom boolean
|
|
|
|
:documentation
|
|
|
|
"When t, searches through this engine will never be parsed or
|
|
|
|
transformed, and must be entered \"raw\"."))
|
|
|
|
:abstract t
|
|
|
|
:documentation "Abstract base class for Gnus search engines.")
|
|
|
|
|
|
|
|
(defclass gnus-search-grep ()
|
|
|
|
((grep-program
|
|
|
|
:initarg :grep-program
|
|
|
|
:initform "grep"
|
|
|
|
:type string
|
|
|
|
:documentation "Grep executable to use for second-pass grep
|
|
|
|
searches.")
|
|
|
|
(grep-options
|
|
|
|
:initarg :grep-options
|
|
|
|
:initform nil
|
|
|
|
:type list
|
|
|
|
:documentation "Additional options, in the form of a list,
|
|
|
|
passed to the second-pass grep search, when present."))
|
|
|
|
:abstract t
|
|
|
|
:documentation "An abstract mixin class that can be added to
|
|
|
|
local-filesystem search engines, providing an additional grep:
|
|
|
|
search key. After the base engine returns a list of search
|
|
|
|
results (as local filenames), an external grep process is used
|
|
|
|
to further filter the results.")
|
|
|
|
|
|
|
|
(cl-defgeneric gnus-search-grep-search (engine artlist criteria)
|
|
|
|
"Run a secondary grep search over a list of preliminary results.
|
|
|
|
|
|
|
|
ARTLIST is a list of (filename score) pairs, produced by one of
|
|
|
|
the other search engines. CRITERIA is a grep-specific search
|
|
|
|
key. This method uses an external grep program to further filter
|
|
|
|
the files in ARTLIST by that search key.")
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-grep-search ((engine gnus-search-grep)
|
|
|
|
artlist criteria)
|
|
|
|
(with-slots (grep-program grep-options) engine
|
|
|
|
(if (executable-find grep-program)
|
|
|
|
;; Don't catch errors -- allow them to propagate.
|
|
|
|
(let ((matched-files
|
|
|
|
(apply
|
|
|
|
#'process-lines
|
|
|
|
grep-program
|
|
|
|
`("-l" ,@grep-options
|
|
|
|
"-e" ,(shell-quote-argument criteria)
|
|
|
|
,@(mapcar #'car artlist)))))
|
|
|
|
(seq-filter (lambda (a) (member (car a) matched-files))
|
|
|
|
artlist))
|
|
|
|
(nnheader-report 'search "invalid grep program: %s" grep-program))))
|
|
|
|
|
|
|
|
(defclass gnus-search-process ()
|
|
|
|
((proc-buffer
|
|
|
|
:initarg :proc-buffer
|
|
|
|
:type buffer
|
|
|
|
:documentation "A temporary buffer this engine uses for its
|
|
|
|
search process, and for munging its search results."))
|
|
|
|
:abstract t
|
|
|
|
:documentation
|
|
|
|
"A mixin class for engines that do their searching in a single
|
|
|
|
process launched for this purpose, which returns at the end of
|
|
|
|
the search. Subclass instances are safe to be run in
|
|
|
|
threads.")
|
|
|
|
|
|
|
|
(cl-defmethod shared-initialize ((engine gnus-search-process)
|
|
|
|
slots)
|
|
|
|
(setq slots (plist-put slots :proc-buffer
|
2020-11-07 09:40:40 -08:00
|
|
|
(generate-new-buffer " *gnus-search-")))
|
2020-10-14 21:39:46 -07:00
|
|
|
(cl-call-next-method engine slots))
|
|
|
|
|
2022-02-11 15:09:46 +08:00
|
|
|
(defclass gnus-search-nnselect (gnus-search-engine)
|
|
|
|
nil)
|
|
|
|
|
2020-10-14 21:39:46 -07:00
|
|
|
(defclass gnus-search-imap (gnus-search-engine)
|
|
|
|
((literal-plus
|
|
|
|
:initarg :literal-plus
|
|
|
|
:initform nil
|
|
|
|
:type boolean
|
|
|
|
:documentation
|
|
|
|
"Can this search engine handle literal+ searches? This slot
|
|
|
|
is set automatically by the imap server, and cannot be
|
|
|
|
set manually. Only the LITERAL+ capability is handled.")
|
|
|
|
(multisearch
|
|
|
|
:initarg :multisearch
|
|
|
|
:initform nil
|
|
|
|
:type boolean
|
|
|
|
:documentation
|
|
|
|
"Can this search engine handle the MULTISEARCH capability?
|
|
|
|
This slot is set automatically by the imap server, and cannot
|
|
|
|
be set manually. Currently unimplemented.")
|
|
|
|
(fuzzy
|
|
|
|
:initarg :fuzzy
|
|
|
|
:initform nil
|
|
|
|
:type boolean
|
|
|
|
:documentation
|
|
|
|
"Can this search engine handle the FUZZY search capability?
|
|
|
|
This slot is set automatically by the imap server, and cannot
|
2020-11-03 22:31:42 -08:00
|
|
|
be set manually. Currently only partially implemented.")
|
|
|
|
(raw-queries-p
|
|
|
|
:initform (symbol-value 'gnus-search-imap-raw-queries-p)))
|
2020-10-14 21:39:46 -07:00
|
|
|
:documentation
|
Fix typos
* doc/lispref/display.texi (Size of Displayed Text):
* doc/lispref/windows.texi (Buffer Display Action Functions):
* etc/NEWS:
* etc/ORG-NEWS (Org-Attach has been refactored and extended):
* lisp/battery.el (display-battery-mode, battery--upower-subsribe):
* lisp/calendar/parse-time.el:
* lisp/dired-x.el:
* lisp/emacs-lisp/chart.el (chart-sequece, chart-bar-quickie):
* lisp/emacs-lisp/eldoc.el (eldoc-echo-area-use-multiline-p)
(eldoc-documentation-strategy):
* lisp/emacs-lisp/pcase.el (pcase--split-pred, pcase--u1):
* lisp/gnus/gnus-search.el (gnus-search-expandable-keys)
(gnus-search-parse-query, gnus-search-query-return-string)
(gnus-search-imap, gnus-search-imap-search-command)
(gnus-search-transform-expression):
* lisp/gnus/nnselect.el:
* lisp/isearch.el (isearch-lazy-count-format):
* lisp/mh-e/mh-show.el (mh-show-msg):
* lisp/net/dictionary-connection.el (dictionary-connection-open):
* lisp/net/dictionary.el (dictionary-default-popup-strategy)
(dictionary, dictionary-split-string, dictionary-do-select-dictionary)
(dictionary-display-dictionarys, dictionary-search)
(dictionary-tooltip-mode):
* lisp/net/eudcb-macos-contacts.el (eudc-macos-contacts-set-server):
* lisp/net/mailcap.el (mailcap-mime-data):
* lisp/net/tramp-smb.el (tramp-smb-maybe-open-connection):
* lisp/nxml/nxml-mode.el (nxml-mode):
* lisp/progmodes/cc-engine.el:
* lisp/progmodes/cperl-mode.el (cperl-mode)
(cperl-fontify-syntaxically):
* lisp/progmodes/flymake.el (flymake-diagnostic-functions):
* lisp/progmodes/verilog-mode.el (verilog--supressed-warnings)
(verilog-preprocess):
* lisp/simple.el (self-insert-uses-region-functions):
* lisp/textmodes/bibtex.el (bibtex-copy-summary-as-kill):
* lisp/textmodes/texnfo-upd.el (texinfo-insert-master-menu-list):
* src/dispnew.c:
* src/font.c (Ffont_get):
* src/indent.c (compute_motion):
* src/process.c (init_process_emacs):
* src/w32fns.c (deliver_wm_chars):
* test/lisp/jsonrpc-tests.el (deferred-action-complex-tests):
Fix typos in documentation, comments, and internal identifiers.
2021-02-18 16:41:36 +01:00
|
|
|
"The base IMAP search engine, using an IMAP server's search capabilities.
|
2020-10-14 21:39:46 -07:00
|
|
|
This backend may be subclassed to handle particular IMAP servers'
|
|
|
|
quirks.")
|
|
|
|
|
|
|
|
(defclass gnus-search-find-grep (gnus-search-engine
|
|
|
|
gnus-search-process
|
|
|
|
gnus-search-grep)
|
|
|
|
nil)
|
|
|
|
|
2020-11-03 22:31:42 -08:00
|
|
|
;;; The "indexed" search engine.
|
|
|
|
|
|
|
|
;; These are engines that use an external program, with indexes kept
|
|
|
|
;; on disk, to search messages usually kept in some local directory.
|
|
|
|
;; They have several slots in common, for instance program name or
|
|
|
|
;; configuration file. Many of the subclasses also allow
|
|
|
|
;; distinguishing multiple databases or indexes. These slots can be
|
|
|
|
;; set using a global default, or on a per-server basis.
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(defclass gnus-search-indexed (gnus-search-engine
|
|
|
|
gnus-search-process
|
|
|
|
gnus-search-grep)
|
|
|
|
((program
|
|
|
|
:initarg :program
|
|
|
|
:type string
|
|
|
|
:documentation
|
|
|
|
"The executable used for indexing and searching.")
|
|
|
|
(config-file
|
|
|
|
:init-arg :config-file
|
|
|
|
:type string
|
|
|
|
:custom file
|
|
|
|
:documentation "Location of the config file, if any.")
|
|
|
|
(remove-prefix
|
|
|
|
:initarg :remove-prefix
|
2022-02-14 16:06:14 -08:00
|
|
|
:initform (expand-file-name "Mail/" "~")
|
2020-10-14 21:39:46 -07:00
|
|
|
:type string
|
|
|
|
:documentation
|
|
|
|
"The path to the directory where the indexed mails are
|
2020-11-03 22:31:42 -08:00
|
|
|
kept. This path is removed from the search results.")
|
2020-10-14 21:39:46 -07:00
|
|
|
(switches
|
|
|
|
:initarg :switches
|
|
|
|
:type list
|
|
|
|
:documentation
|
|
|
|
"Additional switches passed to the search engine command-line
|
|
|
|
program."))
|
|
|
|
:abstract t
|
|
|
|
:allow-nil-initform t
|
|
|
|
:documentation "A base search engine class that assumes a local search index
|
|
|
|
accessed by a command line program.")
|
|
|
|
|
|
|
|
(defclass gnus-search-swish-e (gnus-search-indexed)
|
|
|
|
((index-files
|
|
|
|
:init-arg :index-files
|
2020-11-03 22:31:42 -08:00
|
|
|
:initform (symbol-value 'gnus-search-swish-e-index-files)
|
|
|
|
:type list)
|
|
|
|
(program
|
|
|
|
:initform (symbol-value 'gnus-search-swish-e-program))
|
|
|
|
(remove-prefix
|
|
|
|
:initform (symbol-value 'gnus-search-swish-e-remove-prefix))
|
|
|
|
(switches
|
|
|
|
:initform (symbol-value 'gnus-search-swish-e-switches))
|
|
|
|
(raw-queries-p
|
|
|
|
:initform (symbol-value 'gnus-search-swish-e-raw-queries-p))))
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(defclass gnus-search-swish++ (gnus-search-indexed)
|
2020-11-03 22:31:42 -08:00
|
|
|
((program
|
|
|
|
:initform (symbol-value 'gnus-search-swish++-program))
|
|
|
|
(remove-prefix
|
|
|
|
:initform (symbol-value 'gnus-search-swish++-remove-prefix))
|
|
|
|
(switches
|
|
|
|
:initform (symbol-value 'gnus-search-swish++-switches))
|
|
|
|
(config-file
|
|
|
|
:initform (symbol-value 'gnus-search-swish++-config-file))
|
|
|
|
(raw-queries-p
|
|
|
|
:initform (symbol-value 'gnus-search-swish++-raw-queries-p))))
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(defclass gnus-search-mairix (gnus-search-indexed)
|
2020-11-03 22:31:42 -08:00
|
|
|
((program
|
|
|
|
:initform (symbol-value 'gnus-search-mairix-program))
|
|
|
|
(remove-prefix
|
|
|
|
:initform (symbol-value 'gnus-search-mairix-remove-prefix))
|
|
|
|
(switches
|
|
|
|
:initform (symbol-value 'gnus-search-mairix-switches))
|
|
|
|
(config-file
|
|
|
|
:initform (symbol-value 'gnus-search-mairix-config-file))
|
|
|
|
(raw-queries-p
|
|
|
|
:initform (symbol-value 'gnus-search-mairix-raw-queries-p))))
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(defclass gnus-search-namazu (gnus-search-indexed)
|
|
|
|
((index-directory
|
|
|
|
:initarg :index-directory
|
2021-01-23 14:24:09 -08:00
|
|
|
:initform (symbol-value 'gnus-search-namazu-index-directory)
|
2020-10-14 21:39:46 -07:00
|
|
|
:type string
|
2020-11-03 22:31:42 -08:00
|
|
|
:custom directory)
|
|
|
|
(program
|
|
|
|
:initform (symbol-value 'gnus-search-namazu-program))
|
|
|
|
(remove-prefix
|
|
|
|
:initform (symbol-value 'gnus-search-namazu-remove-prefix))
|
|
|
|
(switches
|
|
|
|
:initform (symbol-value 'gnus-search-namazu-switches))
|
|
|
|
(raw-queries-p
|
|
|
|
:initform (symbol-value 'gnus-search-namazu-raw-queries-p))))
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(defclass gnus-search-notmuch (gnus-search-indexed)
|
2020-11-03 22:31:42 -08:00
|
|
|
((program
|
|
|
|
:initform (symbol-value 'gnus-search-notmuch-program))
|
|
|
|
(remove-prefix
|
|
|
|
:initform (symbol-value 'gnus-search-notmuch-remove-prefix))
|
|
|
|
(switches
|
|
|
|
:initform (symbol-value 'gnus-search-notmuch-switches))
|
|
|
|
(config-file
|
|
|
|
:initform (symbol-value 'gnus-search-notmuch-config-file))
|
|
|
|
(raw-queries-p
|
|
|
|
:initform (symbol-value 'gnus-search-notmuch-raw-queries-p))))
|
2020-10-14 21:39:46 -07:00
|
|
|
|
2022-04-07 13:14:01 +02:00
|
|
|
(defclass gnus-search-mu (gnus-search-indexed)
|
|
|
|
((program
|
|
|
|
:initform (symbol-value 'gnus-search-mu-program))
|
|
|
|
(remove-prefix
|
|
|
|
:initform (symbol-value 'gnus-search-mu-remove-prefix))
|
|
|
|
(switches
|
|
|
|
:initform (symbol-value 'gnus-search-mu-switches))
|
|
|
|
(config-directory
|
|
|
|
:initform (symbol-value 'gnus-search-mu-config-directory))
|
|
|
|
(raw-queries-p
|
|
|
|
:initform (symbol-value 'gnus-search-mu-raw-queries-p))))
|
|
|
|
|
2020-10-14 21:39:46 -07:00
|
|
|
(define-obsolete-variable-alias 'nnir-method-default-engines
|
|
|
|
'gnus-search-default-engines "28.1")
|
|
|
|
|
2022-02-11 15:09:46 +08:00
|
|
|
(defcustom gnus-search-default-engines '((nnimap . gnus-search-imap)
|
|
|
|
(nnselect . gnus-search-nnselect))
|
2020-10-14 21:39:46 -07:00
|
|
|
"Alist of default search engines keyed by server method."
|
|
|
|
:version "26.1"
|
2020-11-04 21:13:03 -08:00
|
|
|
:type `(repeat (cons (choice (const nnimap) (const nntp) (const nnspool)
|
2020-10-14 21:39:46 -07:00
|
|
|
(const nneething) (const nndir) (const nnmbox)
|
|
|
|
(const nnml) (const nnmh) (const nndraft)
|
2022-02-11 15:09:46 +08:00
|
|
|
(const nnfolder) (const nnmaildir)
|
|
|
|
(const nnselect))
|
2020-10-14 21:39:46 -07:00
|
|
|
(choice
|
|
|
|
,@(mapcar
|
|
|
|
(lambda (el) (list 'const (intern (car el))))
|
|
|
|
(eieio-build-class-alist 'gnus-search-engine t))))))
|
|
|
|
|
|
|
|
;;; Transforming and running search queries.
|
|
|
|
|
|
|
|
(cl-defgeneric gnus-search-run-search (engine server query groups)
|
|
|
|
"Run QUERY in GROUPS against SERVER, using search ENGINE.
|
|
|
|
Should return results as a vector of vectors.")
|
|
|
|
|
|
|
|
(cl-defgeneric gnus-search-transform (engine expression)
|
|
|
|
"Transform sexp EXPRESSION into a string search query usable by ENGINE.
|
|
|
|
Responsible for handling and, or, and parenthetical expressions.")
|
|
|
|
|
|
|
|
(cl-defgeneric gnus-search-transform-expression (engine expression)
|
|
|
|
"Transform a basic EXPRESSION into a string usable by ENGINE.")
|
|
|
|
|
|
|
|
(cl-defgeneric gnus-search-make-query-string (engine query-spec)
|
|
|
|
"Extract the actual query string to use from QUERY-SPEC.")
|
|
|
|
|
|
|
|
;; Methods that are likely to be the same for all engines.
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-make-query-string ((engine gnus-search-engine)
|
|
|
|
query-spec)
|
2020-11-08 16:32:10 -08:00
|
|
|
(let ((parsed-query (alist-get 'parsed-query query-spec))
|
|
|
|
(raw-query (alist-get 'query query-spec)))
|
|
|
|
(if (and gnus-search-use-parsed-queries
|
|
|
|
(null (alist-get 'raw query-spec))
|
|
|
|
(null (slot-value engine 'raw-queries-p))
|
|
|
|
parsed-query)
|
|
|
|
(gnus-search-transform engine parsed-query)
|
|
|
|
(if (listp raw-query)
|
|
|
|
;; Some callers are sending this in as (query "query"), not
|
|
|
|
;; as a cons cell?
|
|
|
|
(car raw-query)
|
|
|
|
raw-query))))
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(defsubst gnus-search-single-p (query)
|
|
|
|
"Return t if QUERY is a search for a single message."
|
|
|
|
(let ((q (alist-get 'parsed-query query)))
|
|
|
|
(and (= (length q ) 1)
|
|
|
|
(consp (car-safe q))
|
|
|
|
(eq (caar q) 'id))))
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-transform ((engine gnus-search-engine)
|
|
|
|
(query list))
|
|
|
|
(let (clauses)
|
|
|
|
(mapc
|
|
|
|
(lambda (item)
|
|
|
|
(when-let ((expr (gnus-search-transform-expression engine item)))
|
|
|
|
(push expr clauses)))
|
|
|
|
query)
|
|
|
|
(mapconcat #'identity (reverse clauses) " ")))
|
|
|
|
|
|
|
|
;; Most search engines just pass through plain strings.
|
|
|
|
(cl-defmethod gnus-search-transform-expression ((_ gnus-search-engine)
|
|
|
|
(expr string))
|
|
|
|
expr)
|
|
|
|
|
|
|
|
;; Most search engines use implicit ANDs.
|
|
|
|
(cl-defmethod gnus-search-transform-expression ((_ gnus-search-engine)
|
2021-07-20 01:25:01 +00:00
|
|
|
(_expr (eql 'and)))
|
2020-10-14 21:39:46 -07:00
|
|
|
nil)
|
|
|
|
|
|
|
|
;; Most search engines use explicit infixed ORs.
|
|
|
|
(cl-defmethod gnus-search-transform-expression ((engine gnus-search-engine)
|
|
|
|
(expr (head or)))
|
|
|
|
(let ((left (gnus-search-transform-expression engine (nth 1 expr)))
|
|
|
|
(right (gnus-search-transform-expression engine (nth 2 expr))))
|
|
|
|
;; Unhandled keywords return a nil; don't create an "or" expression
|
|
|
|
;; unless both sub-expressions are non-nil.
|
|
|
|
(if (and left right)
|
|
|
|
(format "%s or %s" left right)
|
|
|
|
(or left right))))
|
|
|
|
|
|
|
|
;; Most search engines just use the string "not"
|
|
|
|
(cl-defmethod gnus-search-transform-expression ((engine gnus-search-engine)
|
|
|
|
(expr (head not)))
|
|
|
|
(let ((next (gnus-search-transform-expression engine (cadr expr))))
|
|
|
|
(when next
|
|
|
|
(format "not %s" next))))
|
|
|
|
|
|
|
|
;;; Search Engine Interfaces:
|
|
|
|
|
|
|
|
(autoload 'nnimap-change-group "nnimap")
|
|
|
|
(declare-function nnimap-buffer "nnimap" ())
|
|
|
|
(declare-function nnimap-command "nnimap" (&rest args))
|
|
|
|
|
2020-11-14 13:37:08 -08:00
|
|
|
(defvar gnus-search-imap-search-keys
|
|
|
|
'(body cc bcc from header keyword larger smaller subject text to uid x-gm-raw
|
|
|
|
answered before deleted draft flagged on since recent seen sentbefore
|
|
|
|
senton sentsince unanswered undeleted undraft unflagged unkeyword
|
2021-01-11 09:46:58 -08:00
|
|
|
unseen all old new or not)
|
2020-11-14 13:37:08 -08:00
|
|
|
"Known IMAP search keys.")
|
|
|
|
|
2022-02-11 15:09:46 +08:00
|
|
|
(autoload 'nnselect-categorize "nnselect")
|
|
|
|
(autoload 'nnselect-get-artlist "nnselect" nil nil 'macro)
|
|
|
|
(autoload 'ids-by-group "nnselect")
|
|
|
|
;; nnselect interface
|
|
|
|
(cl-defmethod gnus-search-run-search ((_engine gnus-search-nnselect)
|
|
|
|
_srv query-spec groups)
|
|
|
|
(let ((artlist []))
|
|
|
|
(dolist (group groups)
|
|
|
|
(let* ((gnus-newsgroup-selection (nnselect-get-artlist group))
|
|
|
|
(group-spec
|
|
|
|
(nnselect-categorize
|
|
|
|
(mapcar 'car
|
|
|
|
(ids-by-group
|
|
|
|
(number-sequence 1
|
|
|
|
(length gnus-newsgroup-selection))))
|
|
|
|
(lambda (x)
|
|
|
|
(gnus-group-server x)))))
|
|
|
|
(setq artlist
|
|
|
|
(vconcat artlist
|
|
|
|
(seq-intersection
|
|
|
|
gnus-newsgroup-selection
|
|
|
|
(gnus-search-run-query
|
|
|
|
(list (cons 'search-query-spec query-spec)
|
|
|
|
(cons 'search-group-spec group-spec))))))))
|
|
|
|
artlist))
|
|
|
|
|
|
|
|
|
2020-10-14 21:39:46 -07:00
|
|
|
;; imap interface
|
|
|
|
(cl-defmethod gnus-search-run-search ((engine gnus-search-imap)
|
|
|
|
srv query groups)
|
|
|
|
(save-excursion
|
|
|
|
(let ((server (cadr (gnus-server-to-method srv)))
|
|
|
|
(gnus-inhibit-demon t)
|
|
|
|
;; We're using the message id to look for a single message.
|
|
|
|
(single-search (gnus-search-single-p query))
|
|
|
|
(grouplist (or groups (gnus-search-get-active srv)))
|
|
|
|
q-string artlist group)
|
2021-12-03 09:19:31 -08:00
|
|
|
(gnus-message 7 "Opening server %s" server)
|
2020-12-20 11:26:37 -08:00
|
|
|
(gnus-open-server srv)
|
2020-10-14 21:39:46 -07:00
|
|
|
;; We should only be doing this once, in
|
|
|
|
;; `nnimap-open-connection', but it's too frustrating to try to
|
|
|
|
;; get to the server from the process buffer.
|
|
|
|
(with-current-buffer (nnimap-buffer)
|
|
|
|
(setf (slot-value engine 'literal-plus)
|
|
|
|
(when (nnimap-capability "LITERAL+") t))
|
|
|
|
;; MULTISEARCH not yet implemented.
|
|
|
|
(setf (slot-value engine 'multisearch)
|
|
|
|
(when (nnimap-capability "MULTISEARCH") t))
|
|
|
|
;; FUZZY only partially supported: the command is sent to the
|
|
|
|
;; server (and presumably acted upon), but we don't yet
|
|
|
|
;; request a RELEVANCY score as part of the response.
|
|
|
|
(setf (slot-value engine 'fuzzy)
|
|
|
|
(when (nnimap-capability "SEARCH=FUZZY") t)))
|
|
|
|
|
|
|
|
(setq q-string
|
|
|
|
(gnus-search-make-query-string engine query))
|
|
|
|
|
2020-11-14 13:37:08 -08:00
|
|
|
;; A bit of backward-compatibility slash convenience: if the
|
|
|
|
;; query string doesn't start with any known IMAP search
|
|
|
|
;; keyword, assume it is a "TEXT" search.
|
2021-02-06 09:29:53 -08:00
|
|
|
(unless (or (eql ?\( (aref q-string 0))
|
2021-01-11 09:46:58 -08:00
|
|
|
(and (string-match "\\`[^[:blank:]]+" q-string)
|
|
|
|
(memql (intern-soft (downcase
|
|
|
|
(match-string 0 q-string)))
|
|
|
|
gnus-search-imap-search-keys)))
|
2020-11-14 13:37:08 -08:00
|
|
|
(setq q-string (concat "TEXT " q-string)))
|
|
|
|
|
2020-10-14 21:39:46 -07:00
|
|
|
;; If it's a thread query, make sure that all message-id
|
|
|
|
;; searches are also references searches.
|
|
|
|
(when (alist-get 'thread query)
|
|
|
|
(setq q-string
|
|
|
|
(replace-regexp-in-string
|
|
|
|
"HEADER Message-Id \\([^ )]+\\)"
|
|
|
|
"(OR HEADER Message-Id \\1 HEADER References \\1)"
|
|
|
|
q-string)))
|
|
|
|
|
|
|
|
(while (and (setq group (pop grouplist))
|
2021-12-19 06:11:50 +08:00
|
|
|
(or (null single-search) (= 0 (length artlist))))
|
2020-10-14 21:39:46 -07:00
|
|
|
(when (nnimap-change-group
|
|
|
|
(gnus-group-short-name group) server)
|
|
|
|
(with-current-buffer (nnimap-buffer)
|
2021-12-03 09:19:31 -08:00
|
|
|
(gnus-message 7 "Searching %s..." group)
|
2020-10-14 21:39:46 -07:00
|
|
|
(let ((result
|
|
|
|
(gnus-search-imap-search-command engine q-string)))
|
|
|
|
(when (car result)
|
|
|
|
(setq artlist
|
|
|
|
(vconcat
|
|
|
|
(mapcar
|
|
|
|
(lambda (artnum)
|
|
|
|
(let ((artn (string-to-number artnum)))
|
|
|
|
(when (> artn 0)
|
|
|
|
(vector group artn 100))))
|
|
|
|
(cdr (assoc "SEARCH" (cdr result))))
|
|
|
|
artlist))))
|
2021-12-03 09:19:31 -08:00
|
|
|
(gnus-message 7 "Searching %s...done" group))))
|
2020-10-14 21:39:46 -07:00
|
|
|
(nreverse artlist))))
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-imap-search-command ((engine gnus-search-imap)
|
|
|
|
(query string))
|
|
|
|
"Create the IMAP search command for QUERY.
|
Fix typos
* doc/lispref/display.texi (Size of Displayed Text):
* doc/lispref/windows.texi (Buffer Display Action Functions):
* etc/NEWS:
* etc/ORG-NEWS (Org-Attach has been refactored and extended):
* lisp/battery.el (display-battery-mode, battery--upower-subsribe):
* lisp/calendar/parse-time.el:
* lisp/dired-x.el:
* lisp/emacs-lisp/chart.el (chart-sequece, chart-bar-quickie):
* lisp/emacs-lisp/eldoc.el (eldoc-echo-area-use-multiline-p)
(eldoc-documentation-strategy):
* lisp/emacs-lisp/pcase.el (pcase--split-pred, pcase--u1):
* lisp/gnus/gnus-search.el (gnus-search-expandable-keys)
(gnus-search-parse-query, gnus-search-query-return-string)
(gnus-search-imap, gnus-search-imap-search-command)
(gnus-search-transform-expression):
* lisp/gnus/nnselect.el:
* lisp/isearch.el (isearch-lazy-count-format):
* lisp/mh-e/mh-show.el (mh-show-msg):
* lisp/net/dictionary-connection.el (dictionary-connection-open):
* lisp/net/dictionary.el (dictionary-default-popup-strategy)
(dictionary, dictionary-split-string, dictionary-do-select-dictionary)
(dictionary-display-dictionarys, dictionary-search)
(dictionary-tooltip-mode):
* lisp/net/eudcb-macos-contacts.el (eudc-macos-contacts-set-server):
* lisp/net/mailcap.el (mailcap-mime-data):
* lisp/net/tramp-smb.el (tramp-smb-maybe-open-connection):
* lisp/nxml/nxml-mode.el (nxml-mode):
* lisp/progmodes/cc-engine.el:
* lisp/progmodes/cperl-mode.el (cperl-mode)
(cperl-fontify-syntaxically):
* lisp/progmodes/flymake.el (flymake-diagnostic-functions):
* lisp/progmodes/verilog-mode.el (verilog--supressed-warnings)
(verilog-preprocess):
* lisp/simple.el (self-insert-uses-region-functions):
* lisp/textmodes/bibtex.el (bibtex-copy-summary-as-kill):
* lisp/textmodes/texnfo-upd.el (texinfo-insert-master-menu-list):
* src/dispnew.c:
* src/font.c (Ffont_get):
* src/indent.c (compute_motion):
* src/process.c (init_process_emacs):
* src/w32fns.c (deliver_wm_chars):
* test/lisp/jsonrpc-tests.el (deferred-action-complex-tests):
Fix typos in documentation, comments, and internal identifiers.
2021-02-18 16:41:36 +01:00
|
|
|
Currently takes into account support for the LITERAL+ capability.
|
2020-10-14 21:39:46 -07:00
|
|
|
Other capabilities could be tested here."
|
|
|
|
(with-slots (literal-plus) engine
|
2021-10-25 09:39:52 -07:00
|
|
|
(when (and literal-plus
|
|
|
|
(string-match-p "\n" query))
|
2020-10-14 21:39:46 -07:00
|
|
|
(setq query (split-string query "\n")))
|
|
|
|
(cond
|
|
|
|
((consp query)
|
|
|
|
;; We're not really streaming, just need to prevent
|
|
|
|
;; `nnimap-send-command' from waiting for a response.
|
|
|
|
(let* ((nnimap-streaming t)
|
|
|
|
(call
|
|
|
|
(nnimap-send-command
|
|
|
|
"UID SEARCH CHARSET UTF-8 %s"
|
|
|
|
(pop query))))
|
|
|
|
(dolist (l query)
|
|
|
|
(process-send-string (get-buffer-process (current-buffer)) l)
|
|
|
|
(process-send-string (get-buffer-process (current-buffer))
|
|
|
|
(if (nnimap-newlinep nnimap-object)
|
|
|
|
"\n"
|
|
|
|
"\r\n")))
|
|
|
|
(nnimap-get-response call)))
|
|
|
|
(t (nnimap-command "UID SEARCH %s" query)))))
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-transform ((_ gnus-search-imap)
|
|
|
|
(_query null))
|
|
|
|
"ALL")
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-transform-expression ((engine gnus-search-imap)
|
|
|
|
(expr string))
|
|
|
|
(unless (string-match-p "\\`/.+/\\'" expr)
|
|
|
|
;; Also need to check for fuzzy here. Or better, do some
|
|
|
|
;; refactoring of this stuff.
|
|
|
|
(format "TEXT %s"
|
|
|
|
(gnus-search-imap-handle-string engine expr))))
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-transform-expression ((engine gnus-search-imap)
|
|
|
|
(expr (head or)))
|
|
|
|
(let ((left (gnus-search-transform-expression engine (nth 1 expr)))
|
|
|
|
(right (gnus-search-transform-expression engine (nth 2 expr))))
|
|
|
|
(if (and left right)
|
|
|
|
(format "(OR %s %s)"
|
|
|
|
left (format (if (eq 'or (car-safe (nth 2 expr)))
|
|
|
|
"(%s)" "%s")
|
|
|
|
right))
|
|
|
|
(or left right))))
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-transform-expression ((engine gnus-search-imap)
|
|
|
|
(expr (head near)))
|
|
|
|
"Imap searches interpret \"near\" as \"or\"."
|
|
|
|
(setcar expr 'or)
|
|
|
|
(gnus-search-transform-expression engine expr))
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-transform-expression ((engine gnus-search-imap)
|
|
|
|
(expr (head not)))
|
|
|
|
"Transform IMAP NOT.
|
|
|
|
If the term to be negated is a flag, then use the appropriate UN*
|
|
|
|
boolean instead."
|
|
|
|
(if (eql (caadr expr) 'mark)
|
|
|
|
(if (string= (cdadr expr) "new")
|
|
|
|
"OLD"
|
|
|
|
(format "UN%s" (gnus-search-imap-handle-flag (cdadr expr))))
|
|
|
|
(format "NOT %s"
|
|
|
|
(gnus-search-transform-expression engine (cadr expr)))))
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-transform-expression ((_ gnus-search-imap)
|
|
|
|
(expr (head mark)))
|
|
|
|
(gnus-search-imap-handle-flag (cdr expr)))
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-transform-expression ((engine gnus-search-imap)
|
|
|
|
(expr list))
|
|
|
|
"Handle a search keyword for IMAP.
|
|
|
|
All IMAP search keywords that take a value are supported
|
|
|
|
directly. Keywords that are boolean are supported through other
|
|
|
|
means (usually the \"mark\" keyword)."
|
|
|
|
(let ((fuzzy-supported (slot-value engine 'fuzzy))
|
|
|
|
(fuzzy ""))
|
|
|
|
(cl-case (car expr)
|
|
|
|
(date (setcar expr 'on))
|
|
|
|
(tag (setcar expr 'keyword))
|
2020-11-04 21:13:03 -08:00
|
|
|
(sender (setcar expr 'from))
|
|
|
|
(attachment (setcar expr 'body)))
|
|
|
|
;; Allow sizes specified as KB or MB.
|
|
|
|
(let ((case-fold-search t)
|
|
|
|
unit)
|
|
|
|
(when (and (memq (car expr) '(larger smaller))
|
|
|
|
(string-match "\\(kb?\\|mb?\\)\\'" (cdr expr)))
|
|
|
|
(setq unit (match-string 1 (cdr expr)))
|
|
|
|
(setcdr expr
|
|
|
|
(number-to-string
|
|
|
|
(* (string-to-number
|
|
|
|
(string-replace unit "" (cdr expr)))
|
|
|
|
(if (string-prefix-p "k" unit)
|
|
|
|
1024
|
|
|
|
1048576))))))
|
2020-10-14 21:39:46 -07:00
|
|
|
(cond
|
|
|
|
((consp (car expr))
|
|
|
|
(format "(%s)" (gnus-search-transform engine expr)))
|
|
|
|
((eq (car expr) 'recipient)
|
|
|
|
(gnus-search-transform
|
|
|
|
engine (gnus-search-parse-query
|
|
|
|
(format
|
2020-11-04 21:13:03 -08:00
|
|
|
"to:%s or cc:%s or bcc:%s"
|
|
|
|
(cdr expr) (cdr expr) (cdr expr)))))
|
2020-10-14 21:39:46 -07:00
|
|
|
((eq (car expr) 'address)
|
|
|
|
(gnus-search-transform
|
|
|
|
engine (gnus-search-parse-query
|
|
|
|
(format
|
2020-11-04 21:13:03 -08:00
|
|
|
"from:%s or to:%s or cc:%s or bcc:%s"
|
|
|
|
(cdr expr) (cdr expr) (cdr expr) (cdr expr)))))
|
2020-10-14 21:39:46 -07:00
|
|
|
((memq (car expr) '(before since on sentbefore senton sentsince))
|
|
|
|
;; Ignore dates given as strings.
|
|
|
|
(when (listp (cdr expr))
|
|
|
|
(format "%s %s"
|
|
|
|
(upcase (symbol-name (car expr)))
|
|
|
|
(gnus-search-imap-handle-date engine (cdr expr)))))
|
|
|
|
((stringp (cdr expr))
|
|
|
|
;; If the search term starts or ends with "*", remove the
|
|
|
|
;; asterisk. If the engine supports FUZZY, then additionally make
|
|
|
|
;; the search fuzzy.
|
|
|
|
(when (string-match "\\`\\*\\|\\*\\'" (cdr expr))
|
|
|
|
(setcdr expr (replace-regexp-in-string
|
|
|
|
"\\`\\*\\|\\*\\'" "" (cdr expr)))
|
|
|
|
(when fuzzy-supported
|
|
|
|
(setq fuzzy "FUZZY ")))
|
|
|
|
;; If the search term is a regexp, drop the expression altogether.
|
|
|
|
(unless (string-match-p "\\`/.+/\\'" (cdr expr))
|
|
|
|
(cond
|
|
|
|
((memq (car expr) gnus-search-imap-search-keys)
|
|
|
|
(format "%s%s %s"
|
|
|
|
fuzzy
|
|
|
|
(upcase (symbol-name (car expr)))
|
|
|
|
(gnus-search-imap-handle-string engine (cdr expr))))
|
|
|
|
((eq (car expr) 'id)
|
|
|
|
(format "HEADER Message-ID \"%s\"" (cdr expr)))
|
|
|
|
;; Treat what can't be handled as a HEADER search. Probably a bad
|
|
|
|
;; idea.
|
|
|
|
(t (format "%sHEADER %s %s"
|
|
|
|
fuzzy
|
|
|
|
(car expr)
|
|
|
|
(gnus-search-imap-handle-string engine (cdr expr))))))))))
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-imap-handle-date ((_engine gnus-search-imap)
|
2020-12-10 19:52:00 -08:00
|
|
|
(date list))
|
2020-10-14 21:39:46 -07:00
|
|
|
"Turn DATE into a date string recognizable by IMAP.
|
|
|
|
While other search engines can interpret partially-qualified
|
|
|
|
dates such as a plain \"January\", IMAP requires an absolute
|
|
|
|
date.
|
|
|
|
|
|
|
|
DATE is a list of (dd mm yyyy), any element of which could be
|
2020-12-10 19:52:00 -08:00
|
|
|
nil (except that (dd nil yyyy) is not allowed). Massage those
|
|
|
|
numbers into the most recent past occurrence of whichever date
|
|
|
|
elements are present."
|
|
|
|
(pcase-let ((`(,nday ,nmonth ,nyear)
|
2021-12-05 18:25:46 -08:00
|
|
|
(seq-subseq (decode-time) 3 6))
|
2020-12-10 19:52:00 -08:00
|
|
|
(`(,dday ,dmonth ,dyear) date))
|
|
|
|
(unless (and dday dmonth dyear)
|
|
|
|
(unless dday (setq dday 1))
|
|
|
|
(if dyear
|
|
|
|
;; If we have a year, then leave everything else as is or set
|
|
|
|
;; to 1.
|
|
|
|
(setq dmonth (or dmonth 1))
|
|
|
|
(if dmonth
|
|
|
|
(setq dyear
|
|
|
|
(if (or (> dmonth nmonth)
|
|
|
|
(and (= dmonth nmonth)
|
|
|
|
(> dday nday)))
|
|
|
|
;; If our day/month combo is ahead of "now",
|
|
|
|
;; move the year back.
|
|
|
|
(1- nyear)
|
|
|
|
nyear))
|
|
|
|
(setq dmonth 1))))
|
|
|
|
(format-time-string
|
|
|
|
"%e-%b-%Y"
|
2021-12-16 09:40:21 -08:00
|
|
|
(encode-time 0 0 0 dday dmonth dyear))))
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(cl-defmethod gnus-search-imap-handle-string ((engine gnus-search-imap)
|
|
|
|
(str string))
|
|
|
|
(with-slots (literal-plus) engine
|
2022-05-06 10:14:08 -07:00
|
|
|
;; TODO: Figure out how Exchange IMAP servers actually work. They
|
|
|
|
;; do not accept any CHARSET but US-ASCII, but they do report
|
|
|
|
;; Literal+ capability. So what do we do? Will quoted strings
|
|
|
|
;; always work?
|
|
|
|
(if (string-match-p "[^[:ascii:]]" str)
|
2020-10-14 21:39:46 -07:00
|
|
|
;; If LITERAL+ is available, use it and encode string as
|
|
|
|
;; UTF-8.
|
|
|
|
(if literal-plus
|
|
|
|
(format "{%d+}\n%s"
|
|
|
|
(string-bytes str)
|
|
|
|
(encode-coding-string str 'utf-8))
|
|
|
|
;; Otherwise, if the user hasn't already quoted the string,
|
|
|
|
;; quote it for them.
|
|
|
|
(if (string-prefix-p "\"" str)
|
|
|
|
str
|
|
|
|
(format "\"%s\"" str)))
|
|
|
|
str)))
|
|
|
|
|
|
|
|
(defun gnus-search-imap-handle-flag (flag)
|
2021-06-25 20:42:16 -07:00
|
|
|
"Adjust string FLAG to help IMAP recognize it.
|
|
|
|
If it's one of the RFC3501 flags, make sure it's upcased.
|
|
|
|
Otherwise, if FLAG starts with a \"$\", treat as a KEYWORD
|
|
|
|
search. Otherwise, drop the flag."
|
2020-10-14 21:39:46 -07:00
|
|
|
(setq flag
|
|
|
|
(pcase flag
|
|
|
|
("flag" "flagged")
|
|
|
|
("read" "seen")
|
2020-11-25 15:26:06 -08:00
|
|
|
("replied" "answered")
|
2020-10-14 21:39:46 -07:00
|
|
|
(_ flag)))
|
2021-06-25 20:42:16 -07:00
|
|
|
(cond
|
|
|
|
((member flag '("seen" "answered" "deleted" "draft" "flagged" "recent"))
|
|
|
|
(upcase flag))
|
|
|
|
((string-prefix-p "$" flag)
|
|
|
|
(format "KEYWORD %s" flag))
|
|
|
|
;; TODO: Provide a user option to treat *all* marks as a KEYWORDs?
|
|
|
|
(t "")))
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
;;; Methods for the indexed search engines.
|
|
|
|
|
|
|
|
;; First, some common methods.
|
|
|
|
|
2021-05-16 10:19:41 -07:00
|
|
|
(cl-defgeneric gnus-search-indexed-parse-output (engine server query &optional groups)
|
|
|
|
"Parse the results of ENGINE's QUERY against SERVER in GROUPS.
|
2020-10-14 21:39:46 -07:00
|
|
|
Locally-indexed search engines return results as a list of
|
|
|
|
filenames, sometimes with additional information. Returns a list
|
|
|
|
of viable results, in the form of a list of [group article score]
|
|
|
|
vectors.")
|
|
|
|
|
2020-11-11 09:34:53 -08:00
|
|
|
(cl-defgeneric gnus-search-indexed-extract (engine)
|
2020-10-14 21:39:46 -07:00
|
|
|
"Extract a single article result from the current buffer.
|
|
|
|
Returns a list of two values: a file name, and a relevancy score.
|
|
|
|
Advances point to the beginning of the next result.")
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-run-search ((engine gnus-search-indexed)
|
|
|
|
server query groups)
|
|
|
|
"Run QUERY against SERVER using ENGINE.
|
|
|
|
This method is common to all indexed search engines.
|
|
|
|
|
|
|
|
Returns a list of [group article score] vectors."
|
|
|
|
|
2022-02-17 21:28:40 -08:00
|
|
|
(let* ((qstring (gnus-search-make-query-string engine query))
|
|
|
|
(program (slot-value engine 'program))
|
|
|
|
(buffer (slot-value engine 'proc-buffer))
|
|
|
|
(cp-list (gnus-search-indexed-search-command
|
|
|
|
engine qstring query groups))
|
|
|
|
proc exitstatus)
|
|
|
|
(with-current-buffer buffer
|
2020-10-14 21:39:46 -07:00
|
|
|
(erase-buffer)
|
|
|
|
(if groups
|
2021-12-03 09:19:31 -08:00
|
|
|
(gnus-message 7 "Doing %s query on %s..." program groups)
|
|
|
|
(gnus-message 7 "Doing %s query..." program))
|
2020-10-14 21:39:46 -07:00
|
|
|
(setq proc (apply #'start-process (format "search-%s" server)
|
|
|
|
buffer program cp-list))
|
|
|
|
(while (process-live-p proc)
|
|
|
|
(accept-process-output proc))
|
|
|
|
(setq exitstatus (process-exit-status proc))
|
|
|
|
(if (zerop exitstatus)
|
|
|
|
;; The search results have been put into the current buffer;
|
|
|
|
;; `parse-output' finds them there and returns the article
|
|
|
|
;; list.
|
|
|
|
(gnus-search-indexed-parse-output engine server query groups)
|
|
|
|
(nnheader-report 'search "%s error: %s" program exitstatus)
|
|
|
|
;; Failure reason is in this buffer, show it if the user
|
|
|
|
;; wants it.
|
|
|
|
(when (> gnus-verbose 6)
|
|
|
|
(display-buffer buffer))
|
2022-02-17 21:28:40 -08:00
|
|
|
nil))))
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(cl-defmethod gnus-search-indexed-parse-output ((engine gnus-search-indexed)
|
|
|
|
server query &optional groups)
|
2021-06-26 10:16:19 -07:00
|
|
|
(let ((prefix (or (slot-value engine 'remove-prefix)
|
|
|
|
""))
|
2021-08-05 17:53:05 -07:00
|
|
|
(groups (mapcar #'gnus-group-short-name groups))
|
2021-06-26 10:16:19 -07:00
|
|
|
artlist article group)
|
2020-10-14 21:39:46 -07:00
|
|
|
(goto-char (point-min))
|
2021-06-26 10:16:19 -07:00
|
|
|
;; Prep prefix, we want to at least be removing the root
|
|
|
|
;; filesystem separator.
|
|
|
|
(when (stringp prefix)
|
|
|
|
(setq prefix (file-name-as-directory
|
|
|
|
(expand-file-name prefix "/"))))
|
2021-05-21 13:35:38 -07:00
|
|
|
(while (not (or (eobp)
|
|
|
|
(looking-at-p
|
|
|
|
"\\(?:[[:space:]\n]+\\)?Process .+ finished")))
|
2020-10-14 21:39:46 -07:00
|
|
|
(pcase-let ((`(,f-name ,score) (gnus-search-indexed-extract engine)))
|
2021-05-21 13:35:38 -07:00
|
|
|
(when (and f-name
|
|
|
|
(file-readable-p f-name)
|
2021-06-26 10:16:19 -07:00
|
|
|
(null (file-directory-p f-name)))
|
2022-02-18 08:37:33 -08:00
|
|
|
;; `expand-file-name' canoncalizes the file name,
|
|
|
|
;; specifically collapsing multiple consecutive directory
|
|
|
|
;; separators.
|
|
|
|
(setq f-name (expand-file-name f-name)
|
|
|
|
group
|
|
|
|
(delete
|
|
|
|
"" ; forward slash at root leaves an empty string
|
|
|
|
(file-name-split
|
2021-06-26 10:16:19 -07:00
|
|
|
(replace-regexp-in-string
|
2022-02-18 08:37:33 -08:00
|
|
|
"\\`\\." "" ; why do we do this?
|
|
|
|
(string-remove-prefix
|
2021-06-26 10:16:19 -07:00
|
|
|
prefix (file-name-directory f-name))
|
2022-02-18 08:37:33 -08:00
|
|
|
nil t)))
|
|
|
|
;; Turn file name segments into a Gnus group name.
|
|
|
|
group (mapconcat
|
|
|
|
#'identity
|
|
|
|
(if (member (car (last group))
|
|
|
|
'("new" "tmp" "cur"))
|
|
|
|
(nbutlast group)
|
|
|
|
group)
|
|
|
|
"."))
|
2021-06-26 10:16:19 -07:00
|
|
|
(setq article (file-name-nondirectory f-name)
|
|
|
|
article
|
|
|
|
;; TODO: Provide a cleaner way of producing final
|
|
|
|
;; article numbers for the various backends.
|
|
|
|
(if (string-match-p "\\`[[:digit:]]+\\'" article)
|
|
|
|
(string-to-number article)
|
|
|
|
(nnmaildir-base-name-to-article-number
|
Use string-search instead of string-match[-p]
`string-search` is easier to understand, less error-prone, much
faster, does not pollute the regexp cache, and does not mutate global
state. Use it where applicable and obviously safe (erring on the
conservative side).
* admin/authors.el (authors-canonical-file-name)
(authors-scan-change-log):
* lisp/apropos.el (apropos-command)
(apropos-documentation-property, apropos-symbols-internal):
* lisp/arc-mode.el (archive-arc-summarize)
(archive-zoo-summarize):
* lisp/calc/calc-aent.el (math-read-factor):
* lisp/calc/calc-ext.el (math-read-big-expr)
(math-format-nice-expr, math-format-number-fancy):
* lisp/calc/calc-forms.el (math-read-angle-brackets):
* lisp/calc/calc-graph.el (calc-graph-set-range):
* lisp/calc/calc-keypd.el (calc-keypad-press):
* lisp/calc/calc-lang.el (tex, latex, math-read-big-rec):
* lisp/calc/calc-prog.el (calc-fix-token-name)
(calc-user-define-permanent, math-define-exp):
* lisp/calc/calc.el (calc-record, calcDigit-key)
(calc-count-lines):
* lisp/calc/calcalg2.el (calc-solve-for, calc-poly-roots)
(math-do-integral):
* lisp/calc/calcalg3.el (calc-find-root, calc-find-minimum)
(calc-get-fit-variables):
* lisp/cedet/ede/speedbar.el (ede-tag-expand):
* lisp/cedet/semantic/java.el (semantic-java-expand-tag):
* lisp/cedet/semantic/sb.el (semantic-sb-show-extra)
(semantic-sb-expand-group):
* lisp/cedet/semantic/wisent/python.el
(semantic-python-instance-variable-p):
* lisp/cus-edit.el (get):
* lisp/descr-text.el (describe-text-sexp):
* lisp/dired-aux.el (dired-compress-file):
* lisp/dired-x.el (dired-make-relative-symlink):
* lisp/dired.el (dired-glob-regexp):
* lisp/dos-fns.el (dos-convert-standard-filename, dos-8+3-filename):
* lisp/edmacro.el (edmacro-format-keys):
* lisp/emacs-lisp/eieio-opt.el (eieio-sb-expand):
* lisp/emacs-lisp/eieio-speedbar.el (eieio-speedbar-object-expand):
* lisp/emacs-lisp/lisp-mnt.el (lm-keywords-list):
* lisp/emacs-lisp/warnings.el (display-warning):
* lisp/emulation/viper-ex.el (viper-ex-read-file-name)
(ex-print-display-lines):
* lisp/env.el (read-envvar-name, setenv):
* lisp/epa-mail.el (epa-mail-encrypt):
* lisp/epg.el (epg--start):
* lisp/erc/erc-backend.el (erc-parse-server-response):
* lisp/erc/erc-dcc.el (erc-dcc-member):
* lisp/erc/erc-speedbar.el (erc-speedbar-expand-server)
(erc-speedbar-expand-channel, erc-speedbar-expand-user):
* lisp/erc/erc.el (erc-send-input):
* lisp/eshell/em-glob.el (eshell-glob-entries):
* lisp/eshell/esh-proc.el (eshell-needs-pipe-p):
* lisp/eshell/esh-util.el (eshell-convert):
* lisp/eshell/esh-var.el (eshell-envvar-names):
* lisp/faces.el (x-resolve-font-name):
* lisp/ffap.el (ffap-file-at-point):
* lisp/files.el (wildcard-to-regexp, shell-quote-wildcard-pattern):
* lisp/forms.el (forms--update):
* lisp/frameset.el (frameset-filter-unshelve-param):
* lisp/gnus/gnus-art.el (article-decode-charset):
* lisp/gnus/gnus-kill.el (gnus-kill-parse-rn-kill-file):
* lisp/gnus/gnus-mlspl.el (gnus-group-split-fancy):
* lisp/gnus/gnus-msg.el (gnus-summary-resend-message-insert-gcc)
(gnus-inews-insert-gcc):
* lisp/gnus/gnus-rfc1843.el (rfc1843-decode-article-body):
* lisp/gnus/gnus-search.el (gnus-search-indexed-parse-output)
(gnus-search--complete-key-data):
* lisp/gnus/gnus-spec.el (gnus-parse-simple-format):
* lisp/gnus/gnus-sum.el (gnus-summary-refer-article):
* lisp/gnus/gnus-util.el (gnus-extract-address-components)
(gnus-newsgroup-directory-form):
* lisp/gnus/gnus-uu.el (gnus-uu-grab-view):
* lisp/gnus/gnus.el (gnus-group-native-p, gnus-short-group-name):
* lisp/gnus/message.el (message-check-news-header-syntax)
(message-make-message-id, message-user-mail-address)
(message-make-fqdn, message-get-reply-headers, message-followup):
* lisp/gnus/mm-decode.el (mm-dissect-buffer):
* lisp/gnus/nnheader.el (nnheader-insert):
* lisp/gnus/nnimap.el (nnimap-process-quirk)
(nnimap-imap-ranges-to-gnus-ranges):
* lisp/gnus/nnmaildir.el (nnmaildir--ensure-suffix):
* lisp/gnus/nnmairix.el (nnmairix-determine-original-group-from-path):
* lisp/gnus/nnrss.el (nnrss-match-macro):
* lisp/gnus/nntp.el (nntp-find-group-and-number):
* lisp/help-fns.el (help--symbol-completion-table-affixation):
* lisp/help.el (help-function-arglist):
* lisp/hippie-exp.el (he-concat-directory-file-name):
* lisp/htmlfontify.el (hfy-relstub):
* lisp/ido.el (ido-make-prompt, ido-complete, ido-copy-current-word)
(ido-exhibit):
* lisp/image/image-converter.el (image-convert-p):
* lisp/info-xref.el (info-xref-docstrings):
* lisp/info.el (Info-toc-build, Info-follow-reference)
(Info-backward-node, Info-finder-find-node)
(Info-speedbar-expand-node):
* lisp/international/mule-diag.el (print-fontset-element):
* lisp/language/korea-util.el (default-korean-keyboard):
* lisp/linum.el (linum-after-change):
* lisp/mail/ietf-drums.el (ietf-drums-parse-address):
* lisp/mail/mail-utils.el (mail-dont-reply-to):
* lisp/mail/rfc2047.el (rfc2047-encode-1, rfc2047-decode-string):
* lisp/mail/rfc2231.el (rfc2231-parse-string):
* lisp/mail/rmailkwd.el (rmail-set-label):
* lisp/mail/rmailsum.el (rmail-header-summary):
* lisp/mail/smtpmail.el (smtpmail-maybe-append-domain)
(smtpmail-user-mail-address):
* lisp/mail/uce.el (uce-reply-to-uce):
* lisp/man.el (Man-default-man-entry):
* lisp/mh-e/mh-alias.el (mh-alias-gecos-name)
(mh-alias-minibuffer-confirm-address):
* lisp/mh-e/mh-comp.el (mh-forwarded-letter-subject):
* lisp/mh-e/mh-speed.el (mh-speed-parse-flists-output):
* lisp/mh-e/mh-utils.el (mh-collect-folder-names-filter)
(mh-folder-completion-function):
* lisp/minibuffer.el (completion--make-envvar-table)
(completion-file-name-table, completion-flex-try-completion)
(completion-flex-all-completions):
* lisp/mpc.el (mpc--proc-quote-string, mpc-cmd-special-tag-p)
(mpc-constraints-tag-lookup):
* lisp/net/ange-ftp.el (ange-ftp-send-cmd)
(ange-ftp-allow-child-lookup):
* lisp/net/mailcap.el (mailcap-mime-types):
* lisp/net/mairix.el (mairix-search-thread-this-article):
* lisp/net/pop3.el (pop3-open-server):
* lisp/net/soap-client.el (soap-decode-xs-complex-type):
* lisp/net/socks.el (socks-filter):
* lisp/nxml/nxml-outln.el (nxml-highlighted-qname):
* lisp/nxml/rng-cmpct.el (rng-c-expand-name, rng-c-expand-datatype):
* lisp/nxml/rng-uri.el (rng-uri-file-name-1):
* lisp/obsolete/complete.el (partial-completion-mode)
(PC-do-completion):
* lisp/obsolete/longlines.el (longlines-encode-string):
* lisp/obsolete/nnir.el (nnir-compose-result):
* lisp/obsolete/terminal.el (te-quote-arg-for-sh):
* lisp/obsolete/tpu-edt.el (tpu-check-search-case):
* lisp/obsolete/url-ns.el (isPlainHostName):
* lisp/pcmpl-unix.el (pcomplete/scp):
* lisp/play/dunnet.el (dun-listify-string2, dun-get-path)
(dun-unix-parse, dun-doassign, dun-cat, dun-batch-unix-interface):
* lisp/progmodes/ebnf2ps.el: (ebnf-eps-header-footer-comment):
* lisp/progmodes/gdb-mi.el (gdb-var-delete)
(gdb-speedbar-expand-node, gdbmi-bnf-incomplete-record-result):
* lisp/progmodes/gud.el (gud-find-expr):
* lisp/progmodes/idlw-help.el (idlwave-do-context-help1):
* lisp/progmodes/idlw-shell.el (idlwave-shell-mode)
(idlwave-shell-filter-hidden-output, idlwave-shell-filter):
* lisp/progmodes/idlwave.el (idlwave-skip-label-or-case)
(idlwave-routine-info):
* lisp/progmodes/octave.el (inferior-octave-completion-at-point):
* lisp/progmodes/sh-script.el (sh-add-completer):
* lisp/progmodes/sql.el (defun):
* lisp/progmodes/xscheme.el (xscheme-process-filter):
* lisp/replace.el (query-replace-compile-replacement)
(map-query-replace-regexp):
* lisp/shell.el (shell--command-completion-data)
(shell-environment-variable-completion):
* lisp/simple.el (display-message-or-buffer):
* lisp/speedbar.el (speedbar-dired, speedbar-tag-file)
(speedbar-tag-expand):
* lisp/subr.el (split-string-and-unquote):
* lisp/tar-mode.el (tar-extract):
* lisp/term.el (term-command-hook, serial-read-name):
* lisp/textmodes/bibtex.el (bibtex-print-help-message):
* lisp/textmodes/ispell.el (ispell-lookup-words, ispell-filter)
(ispell-parse-output, ispell-buffer-local-parsing):
* lisp/textmodes/reftex-cite.el (reftex-do-citation):
* lisp/textmodes/reftex-parse.el (reftex-notice-new):
* lisp/textmodes/reftex-ref.el (reftex-show-entry):
* lisp/textmodes/reftex.el (reftex-compile-variables):
* lisp/textmodes/tex-mode.el (tex-send-command)
(tex-start-tex, tex-append):
* lisp/thingatpt.el (thing-at-point-url-at-point):
* lisp/tmm.el (tmm-add-one-shortcut):
* lisp/transient.el (transient-format-key):
* lisp/url/url-auth.el (url-basic-auth)
(url-digest-auth-directory-id-assoc):
* lisp/url/url-news.el (url-news):
* lisp/url/url-util.el (url-parse-query-string):
* lisp/vc/vc-cvs.el (vc-cvs-parse-entry):
* lisp/wid-browse.el (widget-browse-sexp):
* lisp/woman.el (woman-parse-colon-path, woman-mini-help)
(WoMan-getpage-in-background, woman-negative-vertical-space):
* lisp/xml.el:
* test/lisp/emacs-lisp/check-declare-tests.el
(check-declare-tests-warn):
* test/lisp/files-tests.el
(files-tests-file-name-non-special-dired-compress-handler):
* test/lisp/net/network-stream-tests.el (server-process-filter):
* test/src/coding-tests.el (ert-test-unibyte-buffer-dos-eol-decode):
Use `string-search` instead of `string-match` and `string-match-p`.
2021-08-09 11:20:00 +02:00
|
|
|
(substring article 0 (string-search ":" article))
|
2021-06-26 10:16:19 -07:00
|
|
|
group (string-remove-prefix "nnmaildir:" server))))
|
|
|
|
(when (and (numberp article)
|
|
|
|
(or (null groups)
|
|
|
|
(member group groups)))
|
|
|
|
(push (list f-name article group score)
|
|
|
|
artlist)))))
|
2020-10-14 21:39:46 -07:00
|
|
|
;; Are we running an additional grep query?
|
|
|
|
(when-let ((grep-reg (alist-get 'grep query)))
|
|
|
|
(setq artlist (gnus-search-grep-search engine artlist grep-reg)))
|
2021-06-26 10:16:19 -07:00
|
|
|
;; Munge into the list of vectors expected by nnselect.
|
|
|
|
(mapcar (pcase-lambda (`(,_ ,article ,group ,score))
|
2021-08-05 17:53:05 -07:00
|
|
|
(vector
|
|
|
|
(gnus-group-full-name group server)
|
|
|
|
article
|
|
|
|
(if (numberp score)
|
|
|
|
score
|
|
|
|
(string-to-number score))))
|
2021-06-26 10:16:19 -07:00
|
|
|
artlist)))
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(cl-defmethod gnus-search-indexed-extract ((_engine gnus-search-indexed))
|
|
|
|
"Base implementation treats the whole line as a filename, and
|
|
|
|
fudges a relevancy score of 100."
|
|
|
|
(prog1
|
|
|
|
(list (buffer-substring-no-properties (line-beginning-position)
|
|
|
|
(line-end-position))
|
|
|
|
100)
|
|
|
|
(forward-line 1)))
|
|
|
|
|
|
|
|
;; Swish++
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-transform-expression ((engine gnus-search-swish++)
|
|
|
|
(expr (head near)))
|
|
|
|
(format "%s near %s"
|
|
|
|
(gnus-search-transform-expression engine (nth 1 expr))
|
|
|
|
(gnus-search-transform-expression engine (nth 2 expr))))
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-transform-expression ((engine gnus-search-swish++)
|
|
|
|
(expr list))
|
|
|
|
(cond
|
|
|
|
((listp (car expr))
|
|
|
|
(format "(%s)" (gnus-search-transform engine expr)))
|
|
|
|
;; Untested and likely wrong.
|
|
|
|
((and (stringp (cdr expr))
|
|
|
|
(string-prefix-p "(" (cdr expr)))
|
|
|
|
(format "%s = %s" (car expr) (gnus-search-transform
|
|
|
|
engine
|
|
|
|
(gnus-search-parse-query (cdr expr)))))
|
|
|
|
(t (format "%s = %s" (car expr) (cdr expr)))))
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-indexed-search-command ((engine gnus-search-swish++)
|
|
|
|
(qstring string)
|
|
|
|
_query &optional _groups)
|
|
|
|
(with-slots (config-file switches) engine
|
|
|
|
`("--config-file" ,config-file
|
|
|
|
,@switches
|
|
|
|
,qstring
|
|
|
|
)))
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-indexed-extract ((_engine gnus-search-swish++))
|
|
|
|
(when (re-search-forward
|
|
|
|
"\\(^[0-9]+\\) \\([^ ]+\\) [0-9]+ \\(.*\\)$" nil t)
|
|
|
|
(list (match-string 2)
|
|
|
|
(match-string 1))))
|
|
|
|
|
|
|
|
;; Swish-e
|
|
|
|
|
|
|
|
;; I didn't do the query transformation for Swish-e, because the
|
|
|
|
;; program seems no longer to exist.
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-indexed-search-command ((engine gnus-search-swish-e)
|
|
|
|
(qstring string)
|
|
|
|
_query &optional _groups)
|
|
|
|
(with-slots (index-files switches) engine
|
|
|
|
`("-f" ,@index-files
|
|
|
|
,@switches
|
|
|
|
"-w"
|
|
|
|
,qstring
|
|
|
|
)))
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-indexed-extract ((_engine gnus-search-swish-e))
|
|
|
|
(when (re-search-forward
|
|
|
|
"\\(^[0-9]+\\) \\([^ ]+\\) \"\\([^\"]+\\)\" [0-9]+$" nil t)
|
|
|
|
(list (match-string 3)
|
|
|
|
(match-string 1))))
|
|
|
|
|
|
|
|
;; Namazu interface
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-transform-expression ((engine gnus-search-namazu)
|
|
|
|
(expr list))
|
|
|
|
(cond
|
|
|
|
((listp (car expr))
|
|
|
|
(format "(%s)" (gnus-search-transform engine expr)))
|
|
|
|
((eql (car expr) 'body)
|
|
|
|
(cadr expr))
|
|
|
|
;; I have no idea which fields namazu can handle. Just do these
|
|
|
|
;; for now.
|
|
|
|
((memq (car expr) '(subject from to))
|
|
|
|
(format "+%s:%s" (car expr) (cdr expr)))
|
|
|
|
((eql (car expr) 'address)
|
|
|
|
(gnus-search-transform engine `((or (from . ,(cdr expr))
|
|
|
|
(to . ,(cdr expr))))))
|
|
|
|
((eq (car expr) 'id)
|
|
|
|
(format "+message-id:%s" (cdr expr)))
|
|
|
|
(t (ignore-errors (cl-call-next-method)))))
|
|
|
|
|
|
|
|
;; I can't tell if this is actually necessary.
|
|
|
|
(cl-defmethod gnus-search-run-search :around ((_e gnus-search-namazu)
|
|
|
|
_server _query _groups)
|
|
|
|
(let ((process-environment (copy-sequence process-environment)))
|
|
|
|
(setenv "LC_MESSAGES" "C")
|
|
|
|
(cl-call-next-method)))
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-indexed-search-command ((engine gnus-search-namazu)
|
|
|
|
(qstring string)
|
|
|
|
query &optional _groups)
|
|
|
|
(let ((max (alist-get 'limit query)))
|
|
|
|
(with-slots (switches index-directory) engine
|
|
|
|
(append
|
|
|
|
(list "-q" ; don't be verbose
|
|
|
|
"-a" ; show all matches
|
|
|
|
"-s") ; use short format
|
|
|
|
(when max (list (format "--max=%d" max)))
|
|
|
|
switches
|
|
|
|
(list qstring index-directory)))))
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-indexed-extract ((_engine gnus-search-namazu))
|
|
|
|
"Extract a single message result for Namazu.
|
|
|
|
Namazu provides a little more information, for instance a score."
|
|
|
|
|
|
|
|
(when (re-search-forward
|
|
|
|
"^\\([0-9,]+\\.\\).*\\((score: \\([0-9]+\\)\\))\n\\([^ ]+\\)"
|
|
|
|
nil t)
|
2021-02-07 13:46:50 -08:00
|
|
|
(forward-line 1)
|
2020-10-14 21:39:46 -07:00
|
|
|
(list (match-string 4)
|
|
|
|
(match-string 3))))
|
|
|
|
|
|
|
|
;;; Notmuch interface
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-transform ((_engine gnus-search-notmuch)
|
|
|
|
(_query null))
|
|
|
|
"*")
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-transform-expression ((engine gnus-search-notmuch)
|
|
|
|
(expr (head near)))
|
|
|
|
(format "%s near %s"
|
|
|
|
(gnus-search-transform-expression engine (nth 1 expr))
|
|
|
|
(gnus-search-transform-expression engine (nth 2 expr))))
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-transform-expression ((engine gnus-search-notmuch)
|
|
|
|
(expr list))
|
|
|
|
;; Swap keywords as necessary.
|
|
|
|
(cl-case (car expr)
|
|
|
|
(sender (setcar expr 'from))
|
|
|
|
;; Notmuch's "to" is already equivalent to our "recipient".
|
|
|
|
(recipient (setcar expr 'to))
|
|
|
|
(mark (setcar expr 'tag)))
|
|
|
|
;; Then actually format the results.
|
|
|
|
(cl-flet ((notmuch-date (date)
|
|
|
|
(if (stringp date)
|
|
|
|
date
|
|
|
|
(pcase date
|
|
|
|
(`(nil ,m nil)
|
|
|
|
(nth (1- m) gnus-english-month-names))
|
|
|
|
(`(nil nil ,y)
|
|
|
|
(number-to-string y))
|
|
|
|
(`(,d ,m nil)
|
|
|
|
(format "%02d-%02d" d m))
|
|
|
|
(`(nil ,m ,y)
|
|
|
|
(format "%02d-%d" m y))
|
|
|
|
(`(,d ,m ,y)
|
|
|
|
(format "%d/%d/%d" m d y))))))
|
|
|
|
(cond
|
|
|
|
((consp (car expr))
|
|
|
|
(format "(%s)" (gnus-search-transform engine expr)))
|
|
|
|
((eql (car expr) 'address)
|
|
|
|
(gnus-search-transform engine `((or (from . ,(cdr expr))
|
|
|
|
(to . ,(cdr expr))))))
|
|
|
|
((eql (car expr) 'body)
|
|
|
|
(cdr expr))
|
|
|
|
((memq (car expr) '(from to subject attachment mimetype tag id
|
|
|
|
thread folder path lastmod query property))
|
|
|
|
;; Notmuch requires message-id with no angle brackets.
|
|
|
|
(when (eql (car expr) 'id)
|
|
|
|
(setcdr
|
|
|
|
expr (replace-regexp-in-string "\\`<\\|>\\'" "" (cdr expr))))
|
|
|
|
(format "%s:%s" (car expr)
|
|
|
|
(if (string-match "\\`\\*" (cdr expr))
|
|
|
|
;; Notmuch can only handle trailing asterisk
|
|
|
|
;; wildcards, so strip leading asterisks.
|
|
|
|
(replace-match "" nil nil (cdr expr))
|
|
|
|
(cdr expr))))
|
|
|
|
((eq (car expr) 'date)
|
|
|
|
(format "date:%s" (notmuch-date (cdr expr))))
|
|
|
|
((eq (car expr) 'before)
|
|
|
|
(format "date:..%s" (notmuch-date (cdr expr))))
|
|
|
|
((eq (car expr) 'since)
|
|
|
|
(format "date:%s.." (notmuch-date (cdr expr))))
|
|
|
|
(t (ignore-errors (cl-call-next-method))))))
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-indexed-search-command ((engine gnus-search-notmuch)
|
|
|
|
(qstring string)
|
|
|
|
query &optional _groups)
|
|
|
|
;; Theoretically we could use the GROUPS parameter to pass a
|
|
|
|
;; --folder switch to notmuch, but I'm not confident of getting the
|
|
|
|
;; format right.
|
|
|
|
(let ((limit (alist-get 'limit query))
|
|
|
|
(thread (alist-get 'thread query)))
|
|
|
|
(with-slots (switches config-file) engine
|
2022-02-17 13:09:49 -08:00
|
|
|
(append
|
|
|
|
(list (format "--config=%s" config-file)
|
|
|
|
"search"
|
2022-07-07 09:34:04 -07:00
|
|
|
"--output=files")
|
2022-02-17 13:09:49 -08:00
|
|
|
(unless thread '("--duplicate=1"))
|
|
|
|
(when limit (list (format "--limit=%d" limit)))
|
|
|
|
switches
|
2022-07-07 09:34:04 -07:00
|
|
|
(list (if thread
|
|
|
|
(format "thread:\"{%s}\""
|
|
|
|
(string-replace "\"" "\"\"" qstring))
|
|
|
|
qstring))))))
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
;;; Mairix interface
|
|
|
|
|
|
|
|
;; See the Gnus manual for why mairix searching is a bit weird.
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-transform ((engine gnus-search-mairix)
|
|
|
|
(query list))
|
|
|
|
"Transform QUERY for a Mairix engine.
|
|
|
|
Because Mairix doesn't accept parenthesized expressions, nor
|
|
|
|
\"or\" statements between different keys, results may differ from
|
|
|
|
other engines. We unpeel parenthesized expressions, and just
|
|
|
|
cross our fingers for the rest of it."
|
|
|
|
(let (clauses)
|
|
|
|
(mapc
|
|
|
|
(lambda (item)
|
|
|
|
(when-let ((expr (if (consp (car-safe item))
|
|
|
|
(gnus-search-transform engine item)
|
|
|
|
(gnus-search-transform-expression engine item))))
|
|
|
|
(push expr clauses)))
|
|
|
|
query)
|
|
|
|
(mapconcat #'identity (reverse clauses) " ")))
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-transform-expression ((engine gnus-search-mairix)
|
|
|
|
(expr (head not)))
|
|
|
|
"Transform Mairix \"not\".
|
|
|
|
Mairix negation requires a \"~\" preceding string search terms,
|
|
|
|
and \"-\" before marks."
|
|
|
|
(let ((next (gnus-search-transform-expression engine (cadr expr))))
|
Use string-replace instead of replace-regexp-in-string
`string-replace` is easier to understand, less error-prone, much
faster, and results in shorter Lisp and byte code. Use it where
applicable and obviously safe (erring on the conservative side).
* admin/authors.el (authors-scan-change-log):
* lisp/autoinsert.el (auto-insert-alist):
* lisp/calc/calc-prog.el (calc-edit-macro-combine-alg-ent)
(calc-edit-macro-combine-ext-command)
(calc-edit-macro-combine-var-name):
* lisp/calc/calc-units.el (math-make-unit-string):
* lisp/calendar/cal-html.el (cal-html-comment):
* lisp/calendar/cal-tex.el (cal-tex-comment):
* lisp/calendar/icalendar.el (icalendar--convert-string-for-export)
(icalendar--convert-string-for-import):
* lisp/calendar/iso8601.el (iso8601--concat-regexps)
(iso8601--full-time-match, iso8601--combined-match):
* lisp/calendar/time-date.el (format-seconds):
* lisp/calendar/todo-mode.el (todo-filter-items-filename):
* lisp/cedet/cedet-files.el (cedet-directory-name-to-file-name)
(cedet-file-name-to-directory-name):
* lisp/comint.el (comint-watch-for-password-prompt):
* lisp/dired-aux.el (dired-do-chmod):
* lisp/dired-x.el (dired-man):
* lisp/dired.el (dired-insert-directory, dired-goto-file-1):
* lisp/emacs-lisp/comp.el (comp-c-func-name):
* lisp/emacs-lisp/re-builder.el (reb-copy):
* lisp/erc/erc-dcc.el (erc-dcc-unquote-filename):
* lisp/erc/erc.el (erc-quit-reason-zippy, erc-part-reason-zippy)
(erc-update-mode-line-buffer, erc-message-english-PART):
* lisp/files.el (make-backup-file-name-1, files--transform-file-name)
(read-file-modes):
* lisp/fringe.el (fringe-mode):
* lisp/gnus/gnus-art.el (gnus-button-handle-info-url):
* lisp/gnus/gnus-group.el (gnus-group-completing-read):
* lisp/gnus/gnus-icalendar.el (gnus-icalendar-event-from-ical):
* lisp/gnus/gnus-mlspl.el (gnus-group-split-fancy):
* lisp/gnus/gnus-search.el (gnus-search-query-parse-date)
(gnus-search-transform-expression, gnus-search-run-search):
* lisp/gnus/gnus-start.el (gnus-dribble-enter):
* lisp/gnus/gnus-sum.el (gnus-summary-refer-article):
* lisp/gnus/gnus-util.el (gnus-mode-string-quote):
* lisp/gnus/message.el (message-put-addresses-in-ecomplete)
(message-parse-mailto-url, message-mailto-1):
* lisp/gnus/mml-sec.el (mml-secure-epg-sign):
* lisp/gnus/mml-smime.el (mml-smime-epg-verify):
* lisp/gnus/mml2015.el (mml2015-epg-verify):
* lisp/gnus/nnmaildir.el (nnmaildir--system-name)
(nnmaildir-request-list, nnmaildir-retrieve-groups)
(nnmaildir-request-group, nnmaildir-retrieve-headers):
* lisp/gnus/nnrss.el (nnrss-node-text):
* lisp/gnus/spam-report.el (spam-report-gmane-internal)
(spam-report-user-mail-address):
* lisp/ibuffer.el (name):
* lisp/image-dired.el (image-dired-pngnq-thumb)
(image-dired-pngcrush-thumb, image-dired-optipng-thumb)
(image-dired-create-thumb-1):
* lisp/info.el (Info-set-mode-line):
* lisp/international/mule-cmds.el (describe-language-environment):
* lisp/mail/rfc2231.el (rfc2231-parse-string):
* lisp/mail/rfc2368.el (rfc2368-parse-mailto-url):
* lisp/mail/rmail.el (rmail-insert-inbox-text)
(rmail-simplified-subject-regexp):
* lisp/mail/rmailout.el (rmail-output-body-to-file):
* lisp/mail/undigest.el (rmail-digest-rfc1153):
* lisp/man.el (Man-default-man-entry):
* lisp/mouse.el (minor-mode-menu-from-indicator):
* lisp/mpc.el (mpc--debug):
* lisp/net/browse-url.el (browse-url-mail):
* lisp/net/eww.el (eww-update-header-line-format):
* lisp/net/newst-backend.el (newsticker-save-item):
* lisp/net/rcirc.el (rcirc-sentinel):
* lisp/net/soap-client.el (soap-decode-date-time):
* lisp/nxml/rng-cmpct.el (rng-c-literal-2-re):
* lisp/nxml/xmltok.el (let*):
* lisp/obsolete/nnir.el (nnir-run-swish-e, nnir-run-hyrex)
(nnir-run-find-grep):
* lisp/play/dunnet.el (dun-doassign):
* lisp/play/handwrite.el (handwrite):
* lisp/proced.el (proced-format-args):
* lisp/profiler.el (profiler-report-header-line-format):
* lisp/progmodes/gdb-mi.el (gdb-mi-quote):
* lisp/progmodes/make-mode.el (makefile-bsdmake-rule-action-regex)
(makefile-make-font-lock-keywords):
* lisp/progmodes/prolog.el (prolog-guess-fill-prefix):
* lisp/progmodes/ruby-mode.el (ruby-toggle-string-quotes):
* lisp/progmodes/sql.el (sql-remove-tabs-filter, sql-str-literal):
* lisp/progmodes/which-func.el (which-func-current):
* lisp/replace.el (query-replace-read-from)
(occur-engine, replace-quote):
* lisp/select.el (xselect--encode-string):
* lisp/ses.el (ses-export-tab):
* lisp/subr.el (shell-quote-argument):
* lisp/term/pc-win.el (msdos-show-help):
* lisp/term/w32-win.el (w32--set-selection):
* lisp/term/xterm.el (gui-backend-set-selection):
* lisp/textmodes/picture.el (picture-tab-search):
* lisp/thumbs.el (thumbs-call-setroot-command):
* lisp/tooltip.el (tooltip-show-help-non-mode):
* lisp/transient.el (transient-format-key):
* lisp/url/url-mailto.el (url-mailto):
* lisp/vc/log-edit.el (log-edit-changelog-ours-p):
* lisp/vc/vc-bzr.el (vc-bzr-status):
* lisp/vc/vc-hg.el (vc-hg--glob-to-pcre):
* lisp/vc/vc-svn.el (vc-svn-after-dir-status):
* lisp/xdg.el (xdg-desktop-strings):
* test/lisp/electric-tests.el (defun):
* test/lisp/term-tests.el (term-simple-lines):
* test/lisp/time-stamp-tests.el (formatz-mod-del-colons):
* test/lisp/wdired-tests.el (wdired-test-bug32173-01)
(wdired-test-unfinished-edit-01):
* test/src/json-tests.el (json-parse-with-custom-null-and-false-objects):
Use `string-replace` instead of `replace-regexp-in-string`.
2021-08-08 18:58:46 +02:00
|
|
|
(string-replace
|
2020-10-14 21:39:46 -07:00
|
|
|
":"
|
|
|
|
(if (eql (caadr expr) 'mark)
|
|
|
|
":-"
|
|
|
|
":~")
|
|
|
|
next)))
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-transform-expression ((engine gnus-search-mairix)
|
|
|
|
(expr (head or)))
|
|
|
|
"Handle Mairix \"or\" statement.
|
Fix typos
* doc/lispref/display.texi (Size of Displayed Text):
* doc/lispref/windows.texi (Buffer Display Action Functions):
* etc/NEWS:
* etc/ORG-NEWS (Org-Attach has been refactored and extended):
* lisp/battery.el (display-battery-mode, battery--upower-subsribe):
* lisp/calendar/parse-time.el:
* lisp/dired-x.el:
* lisp/emacs-lisp/chart.el (chart-sequece, chart-bar-quickie):
* lisp/emacs-lisp/eldoc.el (eldoc-echo-area-use-multiline-p)
(eldoc-documentation-strategy):
* lisp/emacs-lisp/pcase.el (pcase--split-pred, pcase--u1):
* lisp/gnus/gnus-search.el (gnus-search-expandable-keys)
(gnus-search-parse-query, gnus-search-query-return-string)
(gnus-search-imap, gnus-search-imap-search-command)
(gnus-search-transform-expression):
* lisp/gnus/nnselect.el:
* lisp/isearch.el (isearch-lazy-count-format):
* lisp/mh-e/mh-show.el (mh-show-msg):
* lisp/net/dictionary-connection.el (dictionary-connection-open):
* lisp/net/dictionary.el (dictionary-default-popup-strategy)
(dictionary, dictionary-split-string, dictionary-do-select-dictionary)
(dictionary-display-dictionarys, dictionary-search)
(dictionary-tooltip-mode):
* lisp/net/eudcb-macos-contacts.el (eudc-macos-contacts-set-server):
* lisp/net/mailcap.el (mailcap-mime-data):
* lisp/net/tramp-smb.el (tramp-smb-maybe-open-connection):
* lisp/nxml/nxml-mode.el (nxml-mode):
* lisp/progmodes/cc-engine.el:
* lisp/progmodes/cperl-mode.el (cperl-mode)
(cperl-fontify-syntaxically):
* lisp/progmodes/flymake.el (flymake-diagnostic-functions):
* lisp/progmodes/verilog-mode.el (verilog--supressed-warnings)
(verilog-preprocess):
* lisp/simple.el (self-insert-uses-region-functions):
* lisp/textmodes/bibtex.el (bibtex-copy-summary-as-kill):
* lisp/textmodes/texnfo-upd.el (texinfo-insert-master-menu-list):
* src/dispnew.c:
* src/font.c (Ffont_get):
* src/indent.c (compute_motion):
* src/process.c (init_process_emacs):
* src/w32fns.c (deliver_wm_chars):
* test/lisp/jsonrpc-tests.el (deferred-action-complex-tests):
Fix typos in documentation, comments, and internal identifiers.
2021-02-18 16:41:36 +01:00
|
|
|
Mairix only accepts \"or\" expressions on homogeneous keys. We
|
|
|
|
cast \"or\" expressions on heterogeneous keys as \"and\", which
|
2020-10-14 21:39:46 -07:00
|
|
|
isn't quite right, but it's the best we can do. For date keys,
|
|
|
|
only keep one of the terms."
|
|
|
|
(let ((term1 (caadr expr))
|
|
|
|
(term2 (caaddr expr))
|
|
|
|
(val1 (gnus-search-transform-expression engine (nth 1 expr)))
|
|
|
|
(val2 (gnus-search-transform-expression engine (nth 2 expr))))
|
|
|
|
(cond
|
|
|
|
((or (listp term1) (listp term2))
|
|
|
|
(concat val1 " " val2))
|
|
|
|
((and (member (symbol-name term1) gnus-search-date-keys)
|
|
|
|
(member (symbol-name term2) gnus-search-date-keys))
|
|
|
|
(or val1 val2))
|
|
|
|
((eql term1 term2)
|
|
|
|
(if (and val1 val2)
|
|
|
|
(format "%s/%s"
|
|
|
|
val1
|
|
|
|
(nth 1 (split-string val2 ":")))
|
|
|
|
(or val1 val2)))
|
|
|
|
(t (concat val1 " " val2)))))
|
|
|
|
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-transform-expression ((_ gnus-search-mairix)
|
|
|
|
(expr (head mark)))
|
|
|
|
(gnus-search-mairix-handle-mark (cdr expr)))
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-transform-expression ((engine gnus-search-mairix)
|
|
|
|
(expr list))
|
|
|
|
(let ((key (cl-case (car expr)
|
|
|
|
(sender "f")
|
|
|
|
(from "f")
|
|
|
|
(to "t")
|
|
|
|
(cc "c")
|
|
|
|
(subject "s")
|
|
|
|
(id "m")
|
|
|
|
(body "b")
|
|
|
|
(address "a")
|
|
|
|
(recipient "tc")
|
|
|
|
(text "bs")
|
|
|
|
(attachment "n")
|
|
|
|
(t nil))))
|
|
|
|
(cond
|
|
|
|
((consp (car expr))
|
|
|
|
(gnus-search-transform engine expr))
|
|
|
|
((member (symbol-name (car expr)) gnus-search-date-keys)
|
|
|
|
(gnus-search-mairix-handle-date expr))
|
|
|
|
((memq (car expr) '(size smaller larger))
|
|
|
|
(gnus-search-mairix-handle-size expr))
|
|
|
|
;; Drop regular expressions.
|
|
|
|
((string-match-p "\\`/" (cdr expr))
|
|
|
|
nil)
|
|
|
|
;; Turn parenthesized phrases into multiple word terms. Again,
|
|
|
|
;; this isn't quite what the user is asking for, but better to
|
|
|
|
;; return false positives.
|
|
|
|
((and key (string-match-p "[[:blank:]]" (cdr expr)))
|
|
|
|
(mapconcat
|
|
|
|
(lambda (s) (format "%s:%s" key s))
|
|
|
|
(split-string (gnus-search-mairix-treat-string
|
|
|
|
(cdr expr)))
|
|
|
|
" "))
|
|
|
|
(key (format "%s:%s" key
|
|
|
|
(gnus-search-mairix-treat-string
|
|
|
|
(cdr expr))))
|
|
|
|
(t nil))))
|
|
|
|
|
|
|
|
(defun gnus-search-mairix-treat-string (str)
|
|
|
|
"Treat string for wildcards.
|
|
|
|
Mairix accepts trailing wildcards, but not leading. Also remove
|
|
|
|
double quotes."
|
|
|
|
(replace-regexp-in-string
|
|
|
|
"\\`\\*\\|\"" ""
|
|
|
|
(replace-regexp-in-string "\\*\\'" "=" str)))
|
|
|
|
|
|
|
|
(defun gnus-search-mairix-handle-size (expr)
|
|
|
|
"Format a mairix size search.
|
|
|
|
Assume \"size\" key is equal to \"larger\"."
|
|
|
|
(format
|
|
|
|
(if (eql (car expr) 'smaller)
|
|
|
|
"z:-%s"
|
|
|
|
"z:%s-")
|
|
|
|
(cdr expr)))
|
|
|
|
|
|
|
|
(defun gnus-search-mairix-handle-mark (expr)
|
|
|
|
"Format a mairix mark search."
|
|
|
|
(let ((mark
|
|
|
|
(pcase (cdr expr)
|
|
|
|
("flag" "f")
|
|
|
|
("read" "s")
|
|
|
|
("seen" "s")
|
|
|
|
("replied" "r")
|
|
|
|
(_ nil))))
|
|
|
|
(when mark
|
|
|
|
(format "F:%s" mark))))
|
|
|
|
|
|
|
|
(defun gnus-search-mairix-handle-date (expr)
|
|
|
|
(let ((str
|
|
|
|
(pcase (cdr expr)
|
|
|
|
(`(nil ,m nil)
|
|
|
|
(substring
|
|
|
|
(nth (1- m) gnus-english-month-names)
|
|
|
|
0 3))
|
|
|
|
(`(nil nil ,y)
|
|
|
|
(number-to-string y))
|
|
|
|
(`(,d ,m nil)
|
|
|
|
(format "%s%02d"
|
|
|
|
(substring
|
|
|
|
(nth (1- m) gnus-english-month-names)
|
|
|
|
0 3)
|
|
|
|
d))
|
|
|
|
(`(nil ,m ,y)
|
|
|
|
(format "%d%s"
|
|
|
|
y (substring
|
|
|
|
(nth (1- m) gnus-english-month-names)
|
|
|
|
0 3)))
|
|
|
|
(`(,d ,m ,y)
|
|
|
|
(format "%d%02d%02d" y m d)))))
|
|
|
|
(format
|
|
|
|
(pcase (car expr)
|
|
|
|
('date "d:%s")
|
|
|
|
('since "d:%s-")
|
|
|
|
('after "d:%s-")
|
|
|
|
('before "d:-%s"))
|
|
|
|
str)))
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-indexed-search-command ((engine gnus-search-mairix)
|
|
|
|
(qstring string)
|
|
|
|
query &optional _groups)
|
|
|
|
(with-slots (switches config-file) engine
|
|
|
|
(append `("--rcfile" ,config-file "-r")
|
|
|
|
switches
|
|
|
|
(when (alist-get 'thread query) (list "-t"))
|
|
|
|
(list qstring))))
|
|
|
|
|
2022-04-07 13:14:01 +02:00
|
|
|
;;; Mu interface
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-transform-expression ((engine gnus-search-mu)
|
|
|
|
(expr list))
|
|
|
|
(cl-case (car expr)
|
|
|
|
(recipient (setf (car expr) 'recip))
|
|
|
|
(address (setf (car expr) 'contact))
|
|
|
|
(id (setf (car expr) 'msgid))
|
|
|
|
(attachment (setf (car expr) 'file)))
|
|
|
|
(cl-flet ()
|
|
|
|
(cond
|
|
|
|
((consp (car expr))
|
|
|
|
(format "(%s)" (gnus-search-transform engine expr)))
|
|
|
|
;; Explicitly leave out 'date as gnus-search will encode it
|
|
|
|
;; first; it is handled later
|
|
|
|
((memq (car expr) '(cc c bcc h from f to t subject s body b
|
|
|
|
maildir m msgid i prio p flag g d
|
|
|
|
size z embed e file j mime y tag x
|
|
|
|
list v))
|
|
|
|
(format "%s:%s" (car expr)
|
|
|
|
(if (string-match "\\`\\*" (cdr expr))
|
|
|
|
(replace-match "" nil nil (cdr expr))
|
|
|
|
(cdr expr))))
|
|
|
|
((eq (car expr) 'mark)
|
|
|
|
(format "flag:%s" (gnus-search-mu-handle-flag (cdr expr))))
|
|
|
|
((eq (car expr) 'date)
|
|
|
|
(format "date:%s" (gnus-search-mu-handle-date (cdr expr))))
|
|
|
|
((eq (car expr) 'before)
|
|
|
|
(format "date:..%s" (gnus-search-mu-handle-date (cdr expr))))
|
|
|
|
((eq (car expr) 'since)
|
|
|
|
(format "date:%s.." (gnus-search-mu-handle-date (cdr expr))))
|
|
|
|
(t (ignore-errors (cl-call-next-method))))))
|
|
|
|
|
|
|
|
(defun gnus-search-mu-handle-date (date)
|
|
|
|
(if (stringp date)
|
|
|
|
date
|
|
|
|
(pcase date
|
|
|
|
(`(nil ,m nil)
|
|
|
|
(nth (1- m) gnus-english-month-names))
|
|
|
|
(`(nil nil ,y)
|
|
|
|
(number-to-string y))
|
|
|
|
;; mu prefers ISO date YYYY-MM-DD HH:MM:SS
|
|
|
|
(`(,d ,m nil)
|
|
|
|
(let* ((ct (decode-time))
|
|
|
|
(cm (decoded-time-month ct))
|
|
|
|
(cy (decoded-time-year ct))
|
|
|
|
(y (if (> cm m)
|
|
|
|
cy
|
|
|
|
(1- cy))))
|
|
|
|
(format "%d-%02d-%02d" y m d)))
|
|
|
|
(`(nil ,m ,y)
|
|
|
|
(format "%d-%02d" y m))
|
|
|
|
(`(,d ,m ,y)
|
|
|
|
(format "%d-%02d-%02d" y m d)))))
|
|
|
|
|
|
|
|
(defun gnus-search-mu-handle-flag (flag)
|
|
|
|
;; Only change what doesn't match
|
|
|
|
(cond ((string= flag "flag")
|
|
|
|
"flagged")
|
|
|
|
((string= flag "read")
|
|
|
|
"seen")
|
|
|
|
(t
|
|
|
|
flag)))
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-indexed-extract ((_engine gnus-search-mu))
|
|
|
|
(prog1
|
|
|
|
(let ((bol (line-beginning-position))
|
|
|
|
(eol (line-end-position)))
|
|
|
|
(list (buffer-substring-no-properties bol eol)
|
|
|
|
100))
|
|
|
|
(move-beginning-of-line 2)))
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-indexed-search-command ((engine gnus-search-mu)
|
|
|
|
(qstring string)
|
|
|
|
query &optional groups)
|
|
|
|
(let ((limit (alist-get 'limit query))
|
|
|
|
(thread (alist-get 'thread query)))
|
|
|
|
(with-slots (switches config-directory) engine
|
|
|
|
`("find" ; command must come first
|
|
|
|
"--nocolor" ; mu will always give coloured output otherwise
|
|
|
|
,(format "--muhome=%s" config-directory)
|
|
|
|
,@switches
|
|
|
|
,(if thread "-r" "")
|
|
|
|
,(if limit (format "--maxnum=%d" limit) "")
|
|
|
|
,qstring
|
|
|
|
,@(if groups
|
|
|
|
`("and" "("
|
|
|
|
,@(nbutlast (mapcan (lambda (x)
|
|
|
|
(list (concat "maildir:/" x) "or"))
|
|
|
|
groups))
|
|
|
|
")")
|
|
|
|
"")
|
|
|
|
"--format=plain"
|
|
|
|
"--fields=l"))))
|
|
|
|
|
2020-10-14 21:39:46 -07:00
|
|
|
;;; Find-grep interface
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-transform-expression ((_engine gnus-search-find-grep)
|
|
|
|
(_ list))
|
|
|
|
;; Drop everything that isn't a plain string.
|
|
|
|
nil)
|
|
|
|
|
|
|
|
(cl-defmethod gnus-search-run-search ((engine gnus-search-find-grep)
|
|
|
|
server query
|
|
|
|
&optional groups)
|
|
|
|
"Run find and grep to obtain matching articles."
|
|
|
|
(let* ((method (gnus-server-to-method server))
|
|
|
|
(sym (intern
|
|
|
|
(concat (symbol-name (car method)) "-directory")))
|
|
|
|
(directory (cadr (assoc sym (cddr method))))
|
|
|
|
(regexp (alist-get 'grep query))
|
|
|
|
(grep-options (slot-value engine 'grep-options))
|
|
|
|
(grouplist (or groups (gnus-search-get-active server)))
|
|
|
|
(buffer (slot-value engine 'proc-buffer)))
|
|
|
|
(unless directory
|
2020-11-12 20:03:05 -08:00
|
|
|
(signal 'gnus-search-config-error
|
|
|
|
(list (format-message
|
|
|
|
"No directory found in definition of server %s"
|
|
|
|
server))))
|
2020-10-14 21:39:46 -07:00
|
|
|
(apply
|
2021-01-30 00:35:24 -05:00
|
|
|
#'vconcat
|
2020-10-14 21:39:46 -07:00
|
|
|
(mapcar (lambda (x)
|
|
|
|
(let ((group x)
|
|
|
|
artlist)
|
2021-12-03 09:19:31 -08:00
|
|
|
(gnus-message 7 "Searching %s using find-grep..."
|
|
|
|
(or group server))
|
2020-10-14 21:39:46 -07:00
|
|
|
(save-window-excursion
|
|
|
|
(set-buffer buffer)
|
|
|
|
(if (> gnus-verbose 6)
|
|
|
|
(pop-to-buffer (current-buffer)))
|
|
|
|
(cd directory) ; Using relative paths simplifies
|
|
|
|
; postprocessing.
|
|
|
|
(let ((group
|
|
|
|
(if (not group)
|
|
|
|
"."
|
|
|
|
;; Try accessing the group literally as
|
|
|
|
;; well as interpreting dots as directory
|
|
|
|
;; separators so the engine works with
|
|
|
|
;; plain nnml as well as the Gnus Cache.
|
|
|
|
(let ((group (gnus-group-real-name group)))
|
|
|
|
;; Replace cl-func find-if.
|
|
|
|
(if (file-directory-p group)
|
|
|
|
group
|
|
|
|
(if (file-directory-p
|
|
|
|
(setq group
|
Use string-replace instead of replace-regexp-in-string
`string-replace` is easier to understand, less error-prone, much
faster, and results in shorter Lisp and byte code. Use it where
applicable and obviously safe (erring on the conservative side).
* admin/authors.el (authors-scan-change-log):
* lisp/autoinsert.el (auto-insert-alist):
* lisp/calc/calc-prog.el (calc-edit-macro-combine-alg-ent)
(calc-edit-macro-combine-ext-command)
(calc-edit-macro-combine-var-name):
* lisp/calc/calc-units.el (math-make-unit-string):
* lisp/calendar/cal-html.el (cal-html-comment):
* lisp/calendar/cal-tex.el (cal-tex-comment):
* lisp/calendar/icalendar.el (icalendar--convert-string-for-export)
(icalendar--convert-string-for-import):
* lisp/calendar/iso8601.el (iso8601--concat-regexps)
(iso8601--full-time-match, iso8601--combined-match):
* lisp/calendar/time-date.el (format-seconds):
* lisp/calendar/todo-mode.el (todo-filter-items-filename):
* lisp/cedet/cedet-files.el (cedet-directory-name-to-file-name)
(cedet-file-name-to-directory-name):
* lisp/comint.el (comint-watch-for-password-prompt):
* lisp/dired-aux.el (dired-do-chmod):
* lisp/dired-x.el (dired-man):
* lisp/dired.el (dired-insert-directory, dired-goto-file-1):
* lisp/emacs-lisp/comp.el (comp-c-func-name):
* lisp/emacs-lisp/re-builder.el (reb-copy):
* lisp/erc/erc-dcc.el (erc-dcc-unquote-filename):
* lisp/erc/erc.el (erc-quit-reason-zippy, erc-part-reason-zippy)
(erc-update-mode-line-buffer, erc-message-english-PART):
* lisp/files.el (make-backup-file-name-1, files--transform-file-name)
(read-file-modes):
* lisp/fringe.el (fringe-mode):
* lisp/gnus/gnus-art.el (gnus-button-handle-info-url):
* lisp/gnus/gnus-group.el (gnus-group-completing-read):
* lisp/gnus/gnus-icalendar.el (gnus-icalendar-event-from-ical):
* lisp/gnus/gnus-mlspl.el (gnus-group-split-fancy):
* lisp/gnus/gnus-search.el (gnus-search-query-parse-date)
(gnus-search-transform-expression, gnus-search-run-search):
* lisp/gnus/gnus-start.el (gnus-dribble-enter):
* lisp/gnus/gnus-sum.el (gnus-summary-refer-article):
* lisp/gnus/gnus-util.el (gnus-mode-string-quote):
* lisp/gnus/message.el (message-put-addresses-in-ecomplete)
(message-parse-mailto-url, message-mailto-1):
* lisp/gnus/mml-sec.el (mml-secure-epg-sign):
* lisp/gnus/mml-smime.el (mml-smime-epg-verify):
* lisp/gnus/mml2015.el (mml2015-epg-verify):
* lisp/gnus/nnmaildir.el (nnmaildir--system-name)
(nnmaildir-request-list, nnmaildir-retrieve-groups)
(nnmaildir-request-group, nnmaildir-retrieve-headers):
* lisp/gnus/nnrss.el (nnrss-node-text):
* lisp/gnus/spam-report.el (spam-report-gmane-internal)
(spam-report-user-mail-address):
* lisp/ibuffer.el (name):
* lisp/image-dired.el (image-dired-pngnq-thumb)
(image-dired-pngcrush-thumb, image-dired-optipng-thumb)
(image-dired-create-thumb-1):
* lisp/info.el (Info-set-mode-line):
* lisp/international/mule-cmds.el (describe-language-environment):
* lisp/mail/rfc2231.el (rfc2231-parse-string):
* lisp/mail/rfc2368.el (rfc2368-parse-mailto-url):
* lisp/mail/rmail.el (rmail-insert-inbox-text)
(rmail-simplified-subject-regexp):
* lisp/mail/rmailout.el (rmail-output-body-to-file):
* lisp/mail/undigest.el (rmail-digest-rfc1153):
* lisp/man.el (Man-default-man-entry):
* lisp/mouse.el (minor-mode-menu-from-indicator):
* lisp/mpc.el (mpc--debug):
* lisp/net/browse-url.el (browse-url-mail):
* lisp/net/eww.el (eww-update-header-line-format):
* lisp/net/newst-backend.el (newsticker-save-item):
* lisp/net/rcirc.el (rcirc-sentinel):
* lisp/net/soap-client.el (soap-decode-date-time):
* lisp/nxml/rng-cmpct.el (rng-c-literal-2-re):
* lisp/nxml/xmltok.el (let*):
* lisp/obsolete/nnir.el (nnir-run-swish-e, nnir-run-hyrex)
(nnir-run-find-grep):
* lisp/play/dunnet.el (dun-doassign):
* lisp/play/handwrite.el (handwrite):
* lisp/proced.el (proced-format-args):
* lisp/profiler.el (profiler-report-header-line-format):
* lisp/progmodes/gdb-mi.el (gdb-mi-quote):
* lisp/progmodes/make-mode.el (makefile-bsdmake-rule-action-regex)
(makefile-make-font-lock-keywords):
* lisp/progmodes/prolog.el (prolog-guess-fill-prefix):
* lisp/progmodes/ruby-mode.el (ruby-toggle-string-quotes):
* lisp/progmodes/sql.el (sql-remove-tabs-filter, sql-str-literal):
* lisp/progmodes/which-func.el (which-func-current):
* lisp/replace.el (query-replace-read-from)
(occur-engine, replace-quote):
* lisp/select.el (xselect--encode-string):
* lisp/ses.el (ses-export-tab):
* lisp/subr.el (shell-quote-argument):
* lisp/term/pc-win.el (msdos-show-help):
* lisp/term/w32-win.el (w32--set-selection):
* lisp/term/xterm.el (gui-backend-set-selection):
* lisp/textmodes/picture.el (picture-tab-search):
* lisp/thumbs.el (thumbs-call-setroot-command):
* lisp/tooltip.el (tooltip-show-help-non-mode):
* lisp/transient.el (transient-format-key):
* lisp/url/url-mailto.el (url-mailto):
* lisp/vc/log-edit.el (log-edit-changelog-ours-p):
* lisp/vc/vc-bzr.el (vc-bzr-status):
* lisp/vc/vc-hg.el (vc-hg--glob-to-pcre):
* lisp/vc/vc-svn.el (vc-svn-after-dir-status):
* lisp/xdg.el (xdg-desktop-strings):
* test/lisp/electric-tests.el (defun):
* test/lisp/term-tests.el (term-simple-lines):
* test/lisp/time-stamp-tests.el (formatz-mod-del-colons):
* test/lisp/wdired-tests.el (wdired-test-bug32173-01)
(wdired-test-unfinished-edit-01):
* test/src/json-tests.el (json-parse-with-custom-null-and-false-objects):
Use `string-replace` instead of `replace-regexp-in-string`.
2021-08-08 18:58:46 +02:00
|
|
|
(string-replace
|
|
|
|
"." "/"
|
|
|
|
group)))
|
2020-10-14 21:39:46 -07:00
|
|
|
group))))))
|
|
|
|
(unless group
|
2020-11-12 20:03:05 -08:00
|
|
|
(signal 'gnus-search-config-error
|
|
|
|
(list
|
|
|
|
"Cannot locate directory for group")))
|
2020-10-14 21:39:46 -07:00
|
|
|
(save-excursion
|
|
|
|
(apply
|
2021-01-30 00:35:24 -05:00
|
|
|
#'call-process "find" nil t
|
2020-10-14 21:39:46 -07:00
|
|
|
"find" group "-maxdepth" "1" "-type" "f"
|
|
|
|
"-name" "[0-9]*" "-exec"
|
|
|
|
(slot-value engine 'grep-program)
|
|
|
|
`("-l" ,@(and grep-options
|
|
|
|
(split-string grep-options "\\s-" t))
|
|
|
|
"-e" ,regexp "{}" "+"))))
|
|
|
|
|
|
|
|
;; Translate relative paths to group names.
|
|
|
|
(while (not (eobp))
|
|
|
|
(let* ((path (split-string
|
|
|
|
(buffer-substring
|
|
|
|
(point)
|
2021-01-30 00:35:24 -05:00
|
|
|
(line-end-position))
|
|
|
|
"/" t))
|
2020-10-14 21:39:46 -07:00
|
|
|
(art (string-to-number (car (last path)))))
|
|
|
|
(while (string= "." (car path))
|
|
|
|
(setq path (cdr path)))
|
|
|
|
(let ((group (mapconcat #'identity
|
|
|
|
(cl-subseq path 0 -1)
|
|
|
|
".")))
|
|
|
|
(push
|
|
|
|
(vector (gnus-group-full-name group server) art 0)
|
|
|
|
artlist))
|
|
|
|
(forward-line 1)))
|
2021-12-03 09:19:31 -08:00
|
|
|
(gnus-message 7 "Searching %s using find-grep...done"
|
|
|
|
(or group server))
|
2020-10-14 21:39:46 -07:00
|
|
|
artlist)))
|
|
|
|
grouplist))))
|
|
|
|
|
|
|
|
;;; Util Code:
|
|
|
|
|
|
|
|
(defun gnus-search-run-query (specs)
|
|
|
|
"Invoke appropriate search engine function."
|
|
|
|
;; For now, run the searches synchronously. At some point
|
|
|
|
;; multiple-server searches can each be run in their own thread,
|
|
|
|
;; allowing concurrent searches of multiple backends. At present
|
|
|
|
;; this causes problems when searching more than one server that
|
|
|
|
;; uses `nntp-server-buffer', as their return values are written
|
|
|
|
;; interleaved into that buffer. Anyway, that's the reason for the
|
|
|
|
;; `mapc'.
|
|
|
|
(let* ((results [])
|
|
|
|
(prepared-query (gnus-search-prepare-query
|
|
|
|
(alist-get 'search-query-spec specs)))
|
|
|
|
(limit (alist-get 'limit prepared-query)))
|
|
|
|
(mapc
|
|
|
|
(pcase-lambda (`(,server . ,groups))
|
2020-11-12 20:03:05 -08:00
|
|
|
(condition-case err
|
|
|
|
(let ((search-engine (gnus-search-server-to-engine server)))
|
|
|
|
(setq results
|
|
|
|
(vconcat
|
|
|
|
(gnus-search-run-search
|
|
|
|
search-engine server prepared-query groups)
|
|
|
|
results)))
|
|
|
|
(gnus-search-config-error
|
|
|
|
(if (< 1 (length (alist-get 'search-group-spec specs)))
|
|
|
|
(apply #'nnheader-message 4
|
|
|
|
"Search engine for %s improperly configured: %s"
|
|
|
|
server (cdr err))
|
2021-12-04 13:41:23 -08:00
|
|
|
(signal (car err) (cdr err))))))
|
2020-10-14 21:39:46 -07:00
|
|
|
(alist-get 'search-group-spec specs))
|
|
|
|
;; Some search engines do their own limiting, but some don't, so
|
|
|
|
;; do it again here. This is bad because, if the user is
|
|
|
|
;; searching multiple groups, they would reasonably expect the
|
|
|
|
;; limiting to apply to the search results *after sorting*. Doing
|
|
|
|
;; it this way is liable to, for instance, eliminate all results
|
|
|
|
;; from a later group entirely.
|
|
|
|
(if limit
|
|
|
|
(seq-subseq results 0 (min limit (length results)))
|
2020-11-12 20:03:05 -08:00
|
|
|
results)))
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(defun gnus-search-prepare-query (query-spec)
|
|
|
|
"Accept a search query in raw format, and prepare it.
|
|
|
|
QUERY-SPEC is an alist produced by functions such as
|
2022-04-22 16:17:22 +02:00
|
|
|
`gnus-group-make-search-group', and contains at least a `query'
|
2020-10-14 21:39:46 -07:00
|
|
|
key, and possibly some meta keys. This function extracts any
|
2022-04-22 16:17:22 +02:00
|
|
|
additional meta keys from the `query' string, and parses the
|
2020-10-14 21:39:46 -07:00
|
|
|
remaining string, then adds all that to the top-level spec."
|
|
|
|
(let ((query (alist-get 'query query-spec))
|
|
|
|
val)
|
|
|
|
(when (stringp query)
|
|
|
|
;; Look for these meta keys:
|
|
|
|
(while (string-match
|
|
|
|
"\\(thread\\|grep\\|limit\\|raw\\):\\([^ ]+\\)"
|
|
|
|
query)
|
|
|
|
(setq val (match-string 2 query))
|
|
|
|
(setf (alist-get (intern (match-string 1 query)) query-spec)
|
|
|
|
;; This is stupid.
|
|
|
|
(cond
|
|
|
|
((equal val "t"))
|
|
|
|
((null (zerop (string-to-number val)))
|
|
|
|
(string-to-number val))
|
|
|
|
(t val)))
|
|
|
|
(setq query
|
|
|
|
(string-trim (replace-match "" t t query 0)))
|
|
|
|
(setf (alist-get 'query query-spec) query)))
|
2020-11-08 16:32:10 -08:00
|
|
|
(when (and gnus-search-use-parsed-queries
|
|
|
|
(null (alist-get 'raw query-spec)))
|
2020-10-14 21:39:46 -07:00
|
|
|
(setf (alist-get 'parsed-query query-spec)
|
|
|
|
(gnus-search-parse-query query)))
|
|
|
|
query-spec))
|
|
|
|
|
|
|
|
;; This should be done once at Gnus startup time, when the servers are
|
|
|
|
;; first opened, and the resulting engine instance attached to the
|
|
|
|
;; server.
|
|
|
|
(defun gnus-search-server-to-engine (srv)
|
|
|
|
(let* ((method (gnus-server-to-method srv))
|
2020-11-04 21:13:03 -08:00
|
|
|
(engine-config (assoc 'gnus-search-engine (cddr method)))
|
2020-11-11 10:48:37 -08:00
|
|
|
(server (or (cdr-safe
|
|
|
|
(assoc-string srv gnus-search-engine-instance-alist t))
|
|
|
|
(nth 1 engine-config)
|
2020-11-08 08:37:03 -08:00
|
|
|
(cdr-safe (assoc (car method) gnus-search-default-engines))
|
|
|
|
(when-let ((old (assoc 'nnir-search-engine
|
|
|
|
(cddr method))))
|
|
|
|
(nnheader-message
|
|
|
|
8 "\"nnir-search-engine\" is no longer a valid parameter")
|
|
|
|
(nth 1 old))))
|
|
|
|
inst)
|
|
|
|
(setq server
|
|
|
|
(pcase server
|
|
|
|
('notmuch 'gnus-search-notmuch)
|
|
|
|
('namazu 'gnus-search-namazu)
|
|
|
|
('find-grep 'gnus-search-find-grep)
|
|
|
|
('imap 'gnus-search-imap)
|
|
|
|
(_ server))
|
|
|
|
inst
|
2020-10-14 21:39:46 -07:00
|
|
|
(cond
|
|
|
|
((null server) nil)
|
2020-11-04 21:13:03 -08:00
|
|
|
((eieio-object-p server)
|
|
|
|
server)
|
|
|
|
((class-p server)
|
|
|
|
(make-instance server))
|
2020-11-08 08:37:03 -08:00
|
|
|
(t nil)))
|
2020-10-14 21:39:46 -07:00
|
|
|
(if inst
|
2020-11-11 10:48:37 -08:00
|
|
|
(unless (assoc-string srv gnus-search-engine-instance-alist t)
|
|
|
|
(when (cddr engine-config)
|
|
|
|
;; We're not being completely backward-compatible here,
|
|
|
|
;; because we're not checking for nnir-specific config
|
|
|
|
;; options in the server definition.
|
|
|
|
(pcase-dolist (`(,key ,value) (cddr engine-config))
|
|
|
|
(condition-case nil
|
|
|
|
(setf (slot-value inst key) value)
|
|
|
|
((invalid-slot-name invalid-slot-type)
|
2020-11-12 20:03:05 -08:00
|
|
|
(nnheader-report 'search
|
|
|
|
"Invalid search engine parameter: (%s %s)"
|
2020-11-11 10:48:37 -08:00
|
|
|
key value)))))
|
|
|
|
(push (cons srv inst) gnus-search-engine-instance-alist))
|
2020-11-12 20:03:05 -08:00
|
|
|
(signal 'gnus-search-config-error
|
|
|
|
(list (format-message
|
|
|
|
"No search engine configured for %s" srv))))
|
2020-10-14 21:39:46 -07:00
|
|
|
inst))
|
|
|
|
|
|
|
|
(declare-function gnus-registry-get-id-key "gnus-registry" (id key))
|
|
|
|
|
|
|
|
(defun gnus-search-thread (header)
|
|
|
|
"Make an nnselect group based on the thread containing the article
|
|
|
|
header. The current server will be searched. If the registry is
|
|
|
|
installed, the server that the registry reports the current
|
|
|
|
article came from is also searched."
|
|
|
|
(let* ((ids (cons (mail-header-id header)
|
|
|
|
(split-string
|
|
|
|
(or (mail-header-references header)
|
|
|
|
""))))
|
|
|
|
(query
|
|
|
|
(list (cons 'query (mapconcat (lambda (i)
|
|
|
|
(format "id:%s" i))
|
|
|
|
ids " or "))
|
|
|
|
(cons 'thread t)))
|
|
|
|
(server
|
|
|
|
(list (list (gnus-method-to-server
|
|
|
|
(gnus-find-method-for-group gnus-newsgroup-name)))))
|
|
|
|
(registry-group (and
|
|
|
|
(bound-and-true-p gnus-registry-enabled)
|
|
|
|
(car (gnus-registry-get-id-key
|
|
|
|
(mail-header-id header) 'group))))
|
|
|
|
(registry-server
|
|
|
|
(and registry-group
|
|
|
|
(gnus-method-to-server
|
|
|
|
(gnus-find-method-for-group registry-group)))))
|
|
|
|
(when registry-server
|
|
|
|
(cl-pushnew (list registry-server) server :test #'equal))
|
|
|
|
(gnus-group-make-search-group nil (list
|
|
|
|
(cons 'search-query-spec query)
|
|
|
|
(cons 'search-group-spec server)))
|
|
|
|
(gnus-summary-goto-subject (gnus-id-to-article (mail-header-id header)))))
|
|
|
|
|
|
|
|
(defun gnus-search-get-active (srv)
|
|
|
|
(let ((method (gnus-server-to-method srv))
|
|
|
|
groups)
|
|
|
|
(gnus-request-list method)
|
|
|
|
(with-current-buffer nntp-server-buffer
|
|
|
|
(let ((cur (current-buffer)))
|
|
|
|
(goto-char (point-min))
|
|
|
|
(unless (or (null gnus-search-ignored-newsgroups)
|
|
|
|
(string= gnus-search-ignored-newsgroups ""))
|
|
|
|
(delete-matching-lines gnus-search-ignored-newsgroups))
|
|
|
|
(if (eq (car method) 'nntp)
|
|
|
|
(while (not (eobp))
|
|
|
|
(ignore-errors
|
|
|
|
(push (gnus-group-decoded-name
|
|
|
|
(gnus-group-full-name
|
|
|
|
(buffer-substring
|
|
|
|
(point)
|
|
|
|
(progn
|
|
|
|
(skip-chars-forward "^ \t")
|
|
|
|
(point)))
|
|
|
|
method))
|
|
|
|
groups))
|
|
|
|
(forward-line))
|
|
|
|
(while (not (eobp))
|
|
|
|
(ignore-errors
|
|
|
|
(push (gnus-group-decoded-name
|
|
|
|
(if (eq (char-after) ?\")
|
|
|
|
(gnus-group-full-name (read cur) method)
|
|
|
|
(let ((p (point)) (name ""))
|
|
|
|
(skip-chars-forward "^ \t\\\\")
|
|
|
|
(setq name (buffer-substring p (point)))
|
|
|
|
(while (eq (char-after) ?\\)
|
|
|
|
(setq p (1+ (point)))
|
|
|
|
(forward-char 2)
|
|
|
|
(skip-chars-forward "^ \t\\\\")
|
|
|
|
(setq name (concat name (buffer-substring
|
|
|
|
p (point)))))
|
|
|
|
(gnus-group-full-name name method))))
|
|
|
|
groups))
|
|
|
|
(forward-line)))))
|
|
|
|
groups))
|
|
|
|
|
2022-09-13 15:05:28 +02:00
|
|
|
(defvar-keymap gnus-search-minibuffer-map
|
|
|
|
:parent minibuffer-local-map
|
|
|
|
"TAB" #'completion-at-point)
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(defun gnus-search--complete-key-data ()
|
|
|
|
"Potentially return completion data for a search key or value."
|
|
|
|
(let* ((key-start (save-excursion
|
2020-11-14 13:19:47 -08:00
|
|
|
(or (re-search-backward " " (minibuffer-prompt-end) t)
|
|
|
|
(goto-char (minibuffer-prompt-end)))
|
|
|
|
(skip-chars-forward " -")
|
|
|
|
(point)))
|
2020-10-14 21:39:46 -07:00
|
|
|
(after-colon (save-excursion
|
|
|
|
(when (re-search-backward ":" key-start t)
|
|
|
|
(1+ (point)))))
|
|
|
|
in-string)
|
|
|
|
(if after-colon
|
|
|
|
;; We're in the value part of a key:value pair, which we
|
|
|
|
;; only handle in a contact-completion context.
|
|
|
|
(when (and gnus-search-contact-tables
|
|
|
|
(save-excursion
|
2020-11-14 13:19:47 -08:00
|
|
|
(re-search-backward "\\<-?\\(\\w+\\):" key-start t)
|
2020-10-14 21:39:46 -07:00
|
|
|
(member (match-string 1)
|
|
|
|
'("from" "to" "cc"
|
|
|
|
"bcc" "recipient" "address"))))
|
|
|
|
(setq in-string (nth 3 (syntax-ppss)))
|
|
|
|
(list (if in-string (1+ after-colon) after-colon)
|
|
|
|
(point) (apply #'completion-table-merge
|
|
|
|
gnus-search-contact-tables)
|
|
|
|
:exit-function
|
|
|
|
(lambda (str status)
|
|
|
|
;; If the value contains spaces, make sure it's
|
|
|
|
;; quoted.
|
|
|
|
(when (and (memql status '(exact finished))
|
Use string-search instead of string-match[-p]
`string-search` is easier to understand, less error-prone, much
faster, does not pollute the regexp cache, and does not mutate global
state. Use it where applicable and obviously safe (erring on the
conservative side).
* admin/authors.el (authors-canonical-file-name)
(authors-scan-change-log):
* lisp/apropos.el (apropos-command)
(apropos-documentation-property, apropos-symbols-internal):
* lisp/arc-mode.el (archive-arc-summarize)
(archive-zoo-summarize):
* lisp/calc/calc-aent.el (math-read-factor):
* lisp/calc/calc-ext.el (math-read-big-expr)
(math-format-nice-expr, math-format-number-fancy):
* lisp/calc/calc-forms.el (math-read-angle-brackets):
* lisp/calc/calc-graph.el (calc-graph-set-range):
* lisp/calc/calc-keypd.el (calc-keypad-press):
* lisp/calc/calc-lang.el (tex, latex, math-read-big-rec):
* lisp/calc/calc-prog.el (calc-fix-token-name)
(calc-user-define-permanent, math-define-exp):
* lisp/calc/calc.el (calc-record, calcDigit-key)
(calc-count-lines):
* lisp/calc/calcalg2.el (calc-solve-for, calc-poly-roots)
(math-do-integral):
* lisp/calc/calcalg3.el (calc-find-root, calc-find-minimum)
(calc-get-fit-variables):
* lisp/cedet/ede/speedbar.el (ede-tag-expand):
* lisp/cedet/semantic/java.el (semantic-java-expand-tag):
* lisp/cedet/semantic/sb.el (semantic-sb-show-extra)
(semantic-sb-expand-group):
* lisp/cedet/semantic/wisent/python.el
(semantic-python-instance-variable-p):
* lisp/cus-edit.el (get):
* lisp/descr-text.el (describe-text-sexp):
* lisp/dired-aux.el (dired-compress-file):
* lisp/dired-x.el (dired-make-relative-symlink):
* lisp/dired.el (dired-glob-regexp):
* lisp/dos-fns.el (dos-convert-standard-filename, dos-8+3-filename):
* lisp/edmacro.el (edmacro-format-keys):
* lisp/emacs-lisp/eieio-opt.el (eieio-sb-expand):
* lisp/emacs-lisp/eieio-speedbar.el (eieio-speedbar-object-expand):
* lisp/emacs-lisp/lisp-mnt.el (lm-keywords-list):
* lisp/emacs-lisp/warnings.el (display-warning):
* lisp/emulation/viper-ex.el (viper-ex-read-file-name)
(ex-print-display-lines):
* lisp/env.el (read-envvar-name, setenv):
* lisp/epa-mail.el (epa-mail-encrypt):
* lisp/epg.el (epg--start):
* lisp/erc/erc-backend.el (erc-parse-server-response):
* lisp/erc/erc-dcc.el (erc-dcc-member):
* lisp/erc/erc-speedbar.el (erc-speedbar-expand-server)
(erc-speedbar-expand-channel, erc-speedbar-expand-user):
* lisp/erc/erc.el (erc-send-input):
* lisp/eshell/em-glob.el (eshell-glob-entries):
* lisp/eshell/esh-proc.el (eshell-needs-pipe-p):
* lisp/eshell/esh-util.el (eshell-convert):
* lisp/eshell/esh-var.el (eshell-envvar-names):
* lisp/faces.el (x-resolve-font-name):
* lisp/ffap.el (ffap-file-at-point):
* lisp/files.el (wildcard-to-regexp, shell-quote-wildcard-pattern):
* lisp/forms.el (forms--update):
* lisp/frameset.el (frameset-filter-unshelve-param):
* lisp/gnus/gnus-art.el (article-decode-charset):
* lisp/gnus/gnus-kill.el (gnus-kill-parse-rn-kill-file):
* lisp/gnus/gnus-mlspl.el (gnus-group-split-fancy):
* lisp/gnus/gnus-msg.el (gnus-summary-resend-message-insert-gcc)
(gnus-inews-insert-gcc):
* lisp/gnus/gnus-rfc1843.el (rfc1843-decode-article-body):
* lisp/gnus/gnus-search.el (gnus-search-indexed-parse-output)
(gnus-search--complete-key-data):
* lisp/gnus/gnus-spec.el (gnus-parse-simple-format):
* lisp/gnus/gnus-sum.el (gnus-summary-refer-article):
* lisp/gnus/gnus-util.el (gnus-extract-address-components)
(gnus-newsgroup-directory-form):
* lisp/gnus/gnus-uu.el (gnus-uu-grab-view):
* lisp/gnus/gnus.el (gnus-group-native-p, gnus-short-group-name):
* lisp/gnus/message.el (message-check-news-header-syntax)
(message-make-message-id, message-user-mail-address)
(message-make-fqdn, message-get-reply-headers, message-followup):
* lisp/gnus/mm-decode.el (mm-dissect-buffer):
* lisp/gnus/nnheader.el (nnheader-insert):
* lisp/gnus/nnimap.el (nnimap-process-quirk)
(nnimap-imap-ranges-to-gnus-ranges):
* lisp/gnus/nnmaildir.el (nnmaildir--ensure-suffix):
* lisp/gnus/nnmairix.el (nnmairix-determine-original-group-from-path):
* lisp/gnus/nnrss.el (nnrss-match-macro):
* lisp/gnus/nntp.el (nntp-find-group-and-number):
* lisp/help-fns.el (help--symbol-completion-table-affixation):
* lisp/help.el (help-function-arglist):
* lisp/hippie-exp.el (he-concat-directory-file-name):
* lisp/htmlfontify.el (hfy-relstub):
* lisp/ido.el (ido-make-prompt, ido-complete, ido-copy-current-word)
(ido-exhibit):
* lisp/image/image-converter.el (image-convert-p):
* lisp/info-xref.el (info-xref-docstrings):
* lisp/info.el (Info-toc-build, Info-follow-reference)
(Info-backward-node, Info-finder-find-node)
(Info-speedbar-expand-node):
* lisp/international/mule-diag.el (print-fontset-element):
* lisp/language/korea-util.el (default-korean-keyboard):
* lisp/linum.el (linum-after-change):
* lisp/mail/ietf-drums.el (ietf-drums-parse-address):
* lisp/mail/mail-utils.el (mail-dont-reply-to):
* lisp/mail/rfc2047.el (rfc2047-encode-1, rfc2047-decode-string):
* lisp/mail/rfc2231.el (rfc2231-parse-string):
* lisp/mail/rmailkwd.el (rmail-set-label):
* lisp/mail/rmailsum.el (rmail-header-summary):
* lisp/mail/smtpmail.el (smtpmail-maybe-append-domain)
(smtpmail-user-mail-address):
* lisp/mail/uce.el (uce-reply-to-uce):
* lisp/man.el (Man-default-man-entry):
* lisp/mh-e/mh-alias.el (mh-alias-gecos-name)
(mh-alias-minibuffer-confirm-address):
* lisp/mh-e/mh-comp.el (mh-forwarded-letter-subject):
* lisp/mh-e/mh-speed.el (mh-speed-parse-flists-output):
* lisp/mh-e/mh-utils.el (mh-collect-folder-names-filter)
(mh-folder-completion-function):
* lisp/minibuffer.el (completion--make-envvar-table)
(completion-file-name-table, completion-flex-try-completion)
(completion-flex-all-completions):
* lisp/mpc.el (mpc--proc-quote-string, mpc-cmd-special-tag-p)
(mpc-constraints-tag-lookup):
* lisp/net/ange-ftp.el (ange-ftp-send-cmd)
(ange-ftp-allow-child-lookup):
* lisp/net/mailcap.el (mailcap-mime-types):
* lisp/net/mairix.el (mairix-search-thread-this-article):
* lisp/net/pop3.el (pop3-open-server):
* lisp/net/soap-client.el (soap-decode-xs-complex-type):
* lisp/net/socks.el (socks-filter):
* lisp/nxml/nxml-outln.el (nxml-highlighted-qname):
* lisp/nxml/rng-cmpct.el (rng-c-expand-name, rng-c-expand-datatype):
* lisp/nxml/rng-uri.el (rng-uri-file-name-1):
* lisp/obsolete/complete.el (partial-completion-mode)
(PC-do-completion):
* lisp/obsolete/longlines.el (longlines-encode-string):
* lisp/obsolete/nnir.el (nnir-compose-result):
* lisp/obsolete/terminal.el (te-quote-arg-for-sh):
* lisp/obsolete/tpu-edt.el (tpu-check-search-case):
* lisp/obsolete/url-ns.el (isPlainHostName):
* lisp/pcmpl-unix.el (pcomplete/scp):
* lisp/play/dunnet.el (dun-listify-string2, dun-get-path)
(dun-unix-parse, dun-doassign, dun-cat, dun-batch-unix-interface):
* lisp/progmodes/ebnf2ps.el: (ebnf-eps-header-footer-comment):
* lisp/progmodes/gdb-mi.el (gdb-var-delete)
(gdb-speedbar-expand-node, gdbmi-bnf-incomplete-record-result):
* lisp/progmodes/gud.el (gud-find-expr):
* lisp/progmodes/idlw-help.el (idlwave-do-context-help1):
* lisp/progmodes/idlw-shell.el (idlwave-shell-mode)
(idlwave-shell-filter-hidden-output, idlwave-shell-filter):
* lisp/progmodes/idlwave.el (idlwave-skip-label-or-case)
(idlwave-routine-info):
* lisp/progmodes/octave.el (inferior-octave-completion-at-point):
* lisp/progmodes/sh-script.el (sh-add-completer):
* lisp/progmodes/sql.el (defun):
* lisp/progmodes/xscheme.el (xscheme-process-filter):
* lisp/replace.el (query-replace-compile-replacement)
(map-query-replace-regexp):
* lisp/shell.el (shell--command-completion-data)
(shell-environment-variable-completion):
* lisp/simple.el (display-message-or-buffer):
* lisp/speedbar.el (speedbar-dired, speedbar-tag-file)
(speedbar-tag-expand):
* lisp/subr.el (split-string-and-unquote):
* lisp/tar-mode.el (tar-extract):
* lisp/term.el (term-command-hook, serial-read-name):
* lisp/textmodes/bibtex.el (bibtex-print-help-message):
* lisp/textmodes/ispell.el (ispell-lookup-words, ispell-filter)
(ispell-parse-output, ispell-buffer-local-parsing):
* lisp/textmodes/reftex-cite.el (reftex-do-citation):
* lisp/textmodes/reftex-parse.el (reftex-notice-new):
* lisp/textmodes/reftex-ref.el (reftex-show-entry):
* lisp/textmodes/reftex.el (reftex-compile-variables):
* lisp/textmodes/tex-mode.el (tex-send-command)
(tex-start-tex, tex-append):
* lisp/thingatpt.el (thing-at-point-url-at-point):
* lisp/tmm.el (tmm-add-one-shortcut):
* lisp/transient.el (transient-format-key):
* lisp/url/url-auth.el (url-basic-auth)
(url-digest-auth-directory-id-assoc):
* lisp/url/url-news.el (url-news):
* lisp/url/url-util.el (url-parse-query-string):
* lisp/vc/vc-cvs.el (vc-cvs-parse-entry):
* lisp/wid-browse.el (widget-browse-sexp):
* lisp/woman.el (woman-parse-colon-path, woman-mini-help)
(WoMan-getpage-in-background, woman-negative-vertical-space):
* lisp/xml.el:
* test/lisp/emacs-lisp/check-declare-tests.el
(check-declare-tests-warn):
* test/lisp/files-tests.el
(files-tests-file-name-non-special-dired-compress-handler):
* test/lisp/net/network-stream-tests.el (server-process-filter):
* test/src/coding-tests.el (ert-test-unibyte-buffer-dos-eol-decode):
Use `string-search` instead of `string-match` and `string-match-p`.
2021-08-09 11:20:00 +02:00
|
|
|
(or (string-search " " str)
|
2020-11-11 10:48:37 -08:00
|
|
|
in-string))
|
2020-10-14 21:39:46 -07:00
|
|
|
(unless (looking-at-p "\\s\"")
|
|
|
|
(insert "\""))
|
|
|
|
;; Unless we already have an opening quote...
|
|
|
|
(unless in-string
|
|
|
|
(save-excursion
|
|
|
|
(goto-char after-colon)
|
|
|
|
(insert "\"")))))))
|
|
|
|
(list
|
|
|
|
key-start (point) gnus-search-expandable-keys
|
|
|
|
:exit-function (lambda (_s status)
|
|
|
|
(when (memql status '(exact finished))
|
|
|
|
(insert ":")))))))
|
|
|
|
|
|
|
|
(defun gnus-search-make-spec (arg)
|
|
|
|
(list (cons 'query
|
|
|
|
(minibuffer-with-setup-hook
|
|
|
|
(lambda ()
|
|
|
|
(add-hook 'completion-at-point-functions
|
2020-11-07 09:40:40 -08:00
|
|
|
#'gnus-search--complete-key-data
|
|
|
|
nil t))
|
2020-10-14 21:39:46 -07:00
|
|
|
(read-from-minibuffer
|
|
|
|
"Query: " nil gnus-search-minibuffer-map
|
|
|
|
nil 'gnus-search-history)))
|
2022-02-18 14:01:55 +08:00
|
|
|
(cons 'raw arg)))
|
2020-10-14 21:39:46 -07:00
|
|
|
|
|
|
|
(provide 'gnus-search)
|
|
|
|
;;; gnus-search.el ends here
|