- (let* ((propertized-authors
- ;; Need to save the match data to avoid interfering with
- ;; `notmuch-search-process-filter'.
- (save-match-data
- ;; Authors that don't match the search query are shown in a
- ;; different font.
- (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))))
-
- (formatted-sample (format format-string ""))
- (formatted-authors (format format-string propertized-authors))
- visible-string invisible-string)
-
- ;; Determine the part of the authors that will be visible by
- ;; default.
- (if (> (length formatted-authors)
- (length formatted-sample))
- ;; 4 is `(length "... ")'.
- (let ((visible-length (- (length formatted-sample) 4)))
- (setq visible-string (substring propertized-authors 0 visible-length)
- invisible-string (substring propertized-authors visible-length)))
- (setq visible-string formatted-authors
- invisible-string nil))
-
- ;; Insert both the visible and invisible author strings.
- (insert visible-string)
- (when invisible-string
- (let ((start (point))
- (invis-spec (make-symbol "notmuch-search-authors"))
- overlay)
- (insert invisible-string)
- ;; Using a cons-cell here causes an ellipsis to be inserted
- ;; instead of the invisible text.
- (add-to-invisibility-spec (cons invis-spec t))
- (setq overlay (make-overlay start (point)))
- (overlay-put overlay 'invisible invis-spec)
- (overlay-put overlay 'isearch-open-invisible #'notmuch-search-isearch-authors-show)
- (insert " ")))))
-
-(defun notmuch-search-insert-field (field date count authors subject tags)
- (cond
- ((string-equal field "date")
- (insert (propertize (format (cdr (assoc field notmuch-search-result-format)) date)
- 'face 'notmuch-search-date)))
- ((string-equal field "count")
- (insert (propertize (format (cdr (assoc field notmuch-search-result-format)) count)
- 'face 'notmuch-search-count)))
- ((string-equal field "subject")
- (insert (propertize (format (cdr (assoc field notmuch-search-result-format)) subject)
- 'face 'notmuch-search-subject)))
-
- ((string-equal field "authors")
- (notmuch-search-insert-authors (cdr (assoc field notmuch-search-result-format)) authors))
-
- ((string-equal field "tags")
- (insert (concat "(" (propertize tags 'font-lock-face 'notmuch-tag-face) ")")))))
-
-(defun notmuch-search-show-result (date count authors subject tags)
- (let ((fields) (field))
- (setq fields (mapcar 'car notmuch-search-result-format))
- (loop for field in fields
- do (notmuch-search-insert-field field date count authors subject tags)))
- (insert "\n"))
+ ;; 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.
+ (when (> (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))
+ (setq 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))
+ (setq 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.
+ (progn
+ (setq visible-string (notmuch-search-author-propertize visible-string))
+ ;; The invisible string must contain only non-matching
+ ;; authors, as the visible-string contains both.
+ (setq 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.
+ (setq invisible-string (notmuch-search-author-propertize invisible-string)))
+ ;; If there is any invisible text, add it as a tooltip to the
+ ;; visible text.
+ (unless (string-empty-p 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)
+ (unless (string-empty-p 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 result)
+ (pcase field
+ ((pred functionp)
+ (insert (funcall field format-string result)))
+ ("date"
+ (insert (propertize (format format-string (plist-get result :date_relative))
+ 'face 'notmuch-search-date)))
+ ("count"
+ (insert (propertize (format format-string
+ (format "[%s/%s]" (plist-get result :matched)
+ (plist-get result :total)))
+ 'face 'notmuch-search-count)))
+ ("subject"
+ (insert (propertize (format format-string
+ (notmuch-sanitize (plist-get result :subject)))
+ 'face 'notmuch-search-subject)))
+ ("authors"
+ (notmuch-search-insert-authors format-string
+ (notmuch-sanitize (plist-get result :authors))))
+ ("tags"
+ (let ((tags (plist-get result :tags))
+ (orig-tags (plist-get result :orig-tags)))
+ (insert (format format-string (notmuch-tag-format-tags tags orig-tags)))))))
+
+(defun notmuch-search-show-result (result pos)
+ "Insert RESULT at POS."
+ ;; Ignore excluded matches
+ (unless (= (plist-get result :matched) 0)
+ (save-excursion
+ (goto-char pos)
+ (dolist (spec notmuch-search-result-format)
+ (notmuch-search-insert-field (car spec) (cdr spec) result))
+ (insert "\n")
+ (notmuch-search-color-line pos (point) (plist-get result :tags))
+ (put-text-property pos (point) 'notmuch-search-result result))))
+
+(defun notmuch-search-append-result (result)
+ "Insert RESULT at the end of the buffer.
+
+This is only called when a result is first inserted so it also
+sets the :orig-tag property."
+ (let ((new-result (plist-put result :orig-tags (plist-get result :tags)))
+ (pos (point-max)))
+ (notmuch-search-show-result new-result pos)
+ (when (string= (plist-get result :thread) notmuch-search-target-thread)
+ (setq notmuch-search-target-thread "found")
+ (goto-char pos))))