+ "Colorize lines in `notmuch-show' based on tags."
+ ;; Create the overlay only if the message has tags which match one
+ ;; of those specified in `notmuch-search-line-faces'.
+ (let (overlay)
+ (mapc (lambda (elem)
+ (let ((tag (car elem))
+ (attributes (cdr elem)))
+ (when (member tag line-tag-list)
+ (when (not overlay)
+ (setq overlay (make-overlay start end)))
+ ;; Merge the specified properties with any already
+ ;; applied from an earlier match.
+ (overlay-put overlay 'face
+ (append (overlay-get overlay 'face) attributes)))))
+ notmuch-search-line-faces)))
+
+(defun notmuch-search-author-propertize (authors)
+ "Split `authors' into matching and non-matching authors and
+propertize appropriately. If no boundary between authors and
+non-authors is found, assume that all of the authors match."
+ (if (string-match "\\(.*\\)|\\(.*\\)" authors)
+ (concat (propertize (concat (match-string 1 authors) ",")
+ 'face 'notmuch-search-matching-authors)
+ (propertize (match-string 2 authors)
+ 'face 'notmuch-search-non-matching-authors))
+ (propertize authors 'face 'notmuch-search-matching-authors)))
+
+(defun notmuch-search-insert-authors (format-string authors)
+ ;; Save the match data to avoid interfering with
+ ;; `notmuch-search-process-filter'.
+ (save-match-data
+ (let* ((formatted-authors (format format-string authors))
+ (formatted-sample (format format-string ""))
+ (visible-string formatted-authors)
+ (invisible-string "")
+ (padding ""))
+
+ ;; Truncate the author string to fit the specification.
+ (if (> (length formatted-authors)
+ (length formatted-sample))
+ (let ((visible-length (- (length formatted-sample)
+ (length "... "))))
+ ;; Truncate the visible string according to the width of
+ ;; the display string.
+ (setq visible-string (substring formatted-authors 0 visible-length)
+ invisible-string (substring formatted-authors visible-length))
+ ;; If possible, truncate the visible string at a natural
+ ;; break (comma or pipe), as incremental search doesn't
+ ;; match across the visible/invisible border.
+ (when (string-match "\\(.*\\)\\([,|] \\)\\([^,|]*\\)" visible-string)
+ ;; Second clause is destructive on `visible-string', so
+ ;; order is important.
+ (setq invisible-string (concat (match-string 3 visible-string)
+ invisible-string)
+ visible-string (concat (match-string 1 visible-string)
+ (match-string 2 visible-string))))
+ ;; `visible-string' may be shorter than the space allowed
+ ;; by `format-string'. If so we must insert some padding
+ ;; after `invisible-string'.
+ (setq padding (make-string (- (length formatted-sample)
+ (length visible-string)
+ (length "..."))
+ ? ))))
+
+ ;; Use different faces to show matching and non-matching authors.
+ (if (string-match "\\(.*\\)|\\(.*\\)" visible-string)
+ ;; The visible string contains both matching and
+ ;; non-matching authors.
+ (setq visible-string (notmuch-search-author-propertize visible-string)
+ ;; The invisible string must contain only non-matching
+ ;; authors, as the visible-string contains both.
+ invisible-string (propertize invisible-string
+ 'face 'notmuch-search-non-matching-authors))
+ ;; The visible string contains only matching authors.
+ (setq visible-string (propertize visible-string
+ 'face 'notmuch-search-matching-authors)
+ ;; The invisible string may contain both matching and
+ ;; non-matching authors.
+ invisible-string (notmuch-search-author-propertize invisible-string)))
+
+ ;; If there is any invisible text, add it as a tooltip to the
+ ;; visible text.
+ (when (not (string= invisible-string ""))
+ (setq visible-string (propertize visible-string 'help-echo (concat "..." invisible-string))))
+
+ ;; Insert the visible and, if present, invisible author strings.
+ (insert visible-string)
+ (when (not (string= invisible-string ""))
+ (let ((start (point))
+ overlay)
+ (insert invisible-string)
+ (setq overlay (make-overlay start (point)))
+ (overlay-put overlay 'invisible 'ellipsis)
+ (overlay-put overlay 'isearch-open-invisible #'delete-overlay)))
+ (insert padding))))
+
+(defun notmuch-search-insert-field (field format-string date count authors subject tags)
+ (cond
+ ((string-equal field "date")
+ (insert (propertize (format format-string date)
+ 'face 'notmuch-search-date)))
+ ((string-equal field "count")
+ (insert (propertize (format format-string count)
+ 'face 'notmuch-search-count)))
+ ((string-equal field "subject")
+ (insert (propertize (format format-string subject)
+ 'face 'notmuch-search-subject)))
+
+ ((string-equal field "authors")
+ (notmuch-search-insert-authors format-string authors))
+
+ ((string-equal field "tags")
+ ;; Ignore format-string here because notmuch-search-set-tags
+ ;; depends on the format of this
+ (insert (concat "(" (propertize tags 'font-lock-face 'notmuch-tag-face) ")")))))
+
+(defun notmuch-search-show-result (thread-id date count authors subject tags)
+ ;; Ignore excluded matches
+ (unless (eq (aref count 1) ?0)
+ (let ((beg (point-max))
+ (tags-str (mapconcat 'identity tags " ")))
+ (save-excursion
+ (goto-char beg)
+ (dolist (spec notmuch-search-result-format)
+ (notmuch-search-insert-field (car spec) (cdr spec)
+ date count authors subject tags-str))
+ (insert "\n")
+ (notmuch-search-color-line beg (point) tags)
+ (put-text-property beg (point) 'notmuch-search-thread-id thread-id)
+ (put-text-property beg (point) 'notmuch-search-authors authors)
+ (put-text-property beg (point) 'notmuch-search-subject subject))
+ (when (string= thread-id notmuch-search-target-thread)
+ (setq notmuch-search-target-thread "found")
+ (goto-char beg)))))
+
+(defun notmuch-search-show-error (string &rest objects)
+ (save-excursion
+ (goto-char (point-max))
+ (insert "Error: Unexpected output from notmuch search:\n")
+ (insert (apply #'format string objects))
+ (insert "\n")))