]> git.notmuchmail.org Git - notmuch/blobdiff - emacs/notmuch-hello.el
Merge tag '0.31.4'
[notmuch] / emacs / notmuch-hello.el
index e71e55f3d2ea175b5ac8c3723c26193dec98f0f0..24d2d19e20d4c24e7d77e77312916eab818fcc10 100644 (file)
@@ -1,4 +1,4 @@
-;;; notmuch-hello.el --- welcome to notmuch, a frontend
+;;; notmuch-hello.el --- welcome to notmuch, a frontend  -*- lexical-binding: t -*-
 ;;
 ;; Copyright © David Edmondson
 ;;
@@ -21,8 +21,6 @@
 
 ;;; Code:
 
-(eval-when-compile (require 'cl-lib))
-
 (require 'widget)
 (require 'wid-edit) ; For `widget-forward'.
 
@@ -38,6 +36,8 @@
                  (&optional query query-context target buffer-name open-target))
 
 
+;;; Options
+
 (defun notmuch-saved-search-get (saved-search field)
   "Get FIELD from SAVED-SEARCH.
 
@@ -138,8 +138,8 @@ a plist. Supported properties are
                    shown. If not present then the :query property
                    is used.
   :sort-order      Specify the sort order to be used for the search.
-                   Possible values are 'oldest-first 'newest-first or
-                   nil. Nil means use the default sort order.
+                   Possible values are `oldest-first', `newest-first'
+                   or nil. Nil means use the default sort order.
   :search-type     Specify whether to run the search in search-mode,
                    tree mode or unthreaded mode. Set to 'tree to specify tree
                    mode, 'unthreaded to specify unthreaded mode, and set to nil
@@ -147,9 +147,9 @@ a plist. Supported properties are
 
 Other accepted forms are a cons cell of the form (NAME . QUERY)
 or a list of the form (NAME QUERY COUNT-QUERY)."
-;; The saved-search format is also used by the all-tags notmuch-hello
-;; section. This section generates its own saved-search list in one of
-;; the latter two forms.
+  ;; The saved-search format is also used by the all-tags notmuch-hello
+  ;; section. This section generates its own saved-search list in one of
+  ;; the latter two forms.
   :get 'notmuch-hello--saved-searches-to-plist
   :type '(repeat notmuch-saved-search-plist)
   :tag "List of Saved Searches"
@@ -193,6 +193,8 @@ fields of the search."
 (defvar notmuch-hello-indent 4
   "How much to indent non-headers.")
 
+(defimage notmuch-hello-logo ((:type png :file "notmuch-logo.png")))
+
 (defcustom notmuch-show-logo t
   "Should the notmuch logo be shown?"
   :type 'boolean
@@ -282,7 +284,7 @@ International Bureau of Weights and Measures."
   :group 'notmuch-hello
   :group 'notmuch-hooks)
 
-(defvar notmuch-hello-url "https://notmuchmail.org"
+(defconst notmuch-hello-url "https://notmuchmail.org"
   "The `notmuch' web site.")
 
 (defvar notmuch-hello-custom-section-options
@@ -368,50 +370,68 @@ supported for \"Customized queries section\" items."
   :group 'notmuch-hello
   :type 'boolean)
 
+;;; Internal variables
+
 (defvar notmuch-hello-hidden-sections nil
   "List of sections titles whose contents are hidden.")
 
 (defvar notmuch-hello-first-run t
-  "True if `notmuch-hello' is run for the first time, set to nil
-afterwards.")
-
-(defun notmuch-hello-nice-number (n)
-  (let (result)
-    (while (> n 0)
-      (push (% n 1000) result)
-      (setq n (/ n 1000)))
-    (setq result (or result '(0)))
-    (apply #'concat
-     (number-to-string (car result))
-     (mapcar (lambda (elem)
-             (format "%s%03d" notmuch-hello-thousands-separator elem))
-            (cdr result)))))
-
-(defun notmuch-hello-trim (search)
-  "Trim whitespace."
-  (if (string-match "^[[:space:]]*\\(.*[^[:space:]]\\)[[:space:]]*$" search)
-      (match-string 1 search)
-    search))
-
-(defun notmuch-hello-search (&optional search)
-  (unless (null search)
-    (setq search (notmuch-hello-trim search))
-    (let ((history-delete-duplicates t))
-      (add-to-history 'notmuch-search-history search)))
-  (notmuch-search search notmuch-search-oldest-first))
-
-(defun notmuch-hello-add-saved-search (widget)
-  (interactive)
-  (let ((search (widget-value
-                (symbol-value
-                 (widget-get widget :notmuch-saved-search-widget))))
+  "True if `notmuch-hello' is run for the first time, set to nil afterwards.")
+
+;;; Widgets for inserters
+
+(define-widget 'notmuch-search-item 'item
+  "A recent search."
+  :format "%v\n"
+  :value-create 'notmuch-search-item-value-create)
+
+(defun notmuch-search-item-value-create (widget)
+  (let ((value (widget-get widget :value)))
+    (widget-insert (make-string notmuch-hello-indent ?\s))
+    (widget-create 'editable-field
+                  :size (widget-get widget :size)
+                  :parent widget
+                  :action #'notmuch-hello-search
+                  value)
+    (widget-insert " ")
+    (widget-create 'push-button
+                  :parent widget
+                  :notify #'notmuch-hello-add-saved-search
+                  "save")
+    (widget-insert " ")
+    (widget-create 'push-button
+                  :parent widget
+                  :notify #'notmuch-hello-delete-search-from-history
+                  "del")))
+
+(defun notmuch-search-item-field-width ()
+  (max 8 ; Don't let the search boxes be less than 8 characters wide.
+       (- (window-width)
+         notmuch-hello-indent ; space at bol
+         notmuch-hello-indent ; space at eol
+         1    ; for the space before the [save] button
+         6    ; for the [save] button
+         1    ; for the space before the [del] button
+         5))) ; for the [del] button
+
+;;; Widget actions
+
+(defun notmuch-hello-search (widget &rest _event)
+  (let ((search (widget-value widget)))
+    (when search
+      (setq search (string-trim search))
+      (let ((history-delete-duplicates t))
+       (add-to-history 'notmuch-search-history search)))
+    (notmuch-search search notmuch-search-oldest-first)))
+
+(defun notmuch-hello-add-saved-search (widget &rest _event)
+  (let ((search (widget-value (widget-get widget :parent)))
        (name (completing-read "Name for saved search: "
                               notmuch-saved-searches)))
     ;; If an existing saved search with this name exists, remove it.
     (setq notmuch-saved-searches
          (cl-loop for elem in notmuch-saved-searches
-                  if (not (equal name
-                                 (notmuch-saved-search-get elem :name)))
+                  unless (equal name (notmuch-saved-search-get elem :name))
                   collect elem))
     ;; Add the new one.
     (customize-save-variable 'notmuch-saved-searches
@@ -420,15 +440,20 @@ afterwards.")
     (message "Saved '%s' as '%s'." search name)
     (notmuch-hello-update)))
 
-(defun notmuch-hello-delete-search-from-history (widget)
-  (interactive)
-  (let ((search (widget-value
-                (symbol-value
-                 (widget-get widget :notmuch-saved-search-widget)))))
-    (setq notmuch-search-history (delete search
-                                        notmuch-search-history))
+(defun notmuch-hello-delete-search-from-history (widget &rest _event)
+  (when (y-or-n-p "Are you sure you want to delete this search? ")
+    (let ((search (widget-value (widget-get widget :parent))))
+      (setq notmuch-search-history
+           (delete search notmuch-search-history)))
     (notmuch-hello-update)))
 
+;;; Button utilities
+
+;; `notmuch-hello-query-counts', `notmuch-hello-nice-number' and
+;; `notmuch-hello-insert-buttons' are used outside this section.
+;; All other functions that are defined in this section are only
+;; used by these two functions.
+
 (defun notmuch-hello-longest-label (searches-alist)
   (or (cl-loop for elem in searches-alist
               maximize (length (notmuch-saved-search-get elem :name)))
@@ -453,19 +478,15 @@ diagonal."
     (cl-loop for row from 0 to (- nrows 1)
             append (notmuch-hello-reflect-generate-row ncols nrows row list))))
 
-(defun notmuch-hello-widget-search (widget &rest ignore)
-  (cond
-   ((eq (widget-get widget :notmuch-search-type) 'tree)
-    (notmuch-tree (widget-get widget
-                             :notmuch-search-terms)))
-   ((eq (widget-get widget :notmuch-search-type) 'unthreaded)
-    (notmuch-unthreaded (widget-get widget
-                                   :notmuch-search-terms)))
+(defun notmuch-hello-widget-search (widget &rest _ignore)
+  (cl-case (widget-get widget :notmuch-search-type)
+   (tree
+    (notmuch-tree (widget-get widget :notmuch-search-terms)))
+   (unthreaded
+    (notmuch-unthreaded (widget-get widget :notmuch-search-terms)))
    (t
-    (notmuch-search (widget-get widget
-                               :notmuch-search-terms)
-                   (widget-get widget
-                               :notmuch-search-oldest-first)))))
+    (notmuch-search (widget-get widget :notmuch-search-terms)
+                   (widget-get widget :notmuch-search-oldest-first)))))
 
 (defun notmuch-saved-search-count (search)
   (car (process-lines notmuch-command "count" search)))
@@ -540,7 +561,7 @@ options will be handled as specified for
          (notmuch-hello-filtered-query count-query
                                        (or (plist-get options :filter-count)
                                            (plist-get options :filter))))
-         "\n")))
+        "\n")))
     (unless (= (call-process-region (point-min) (point-max) notmuch-command
                                    t t nil "count" "--batch") 0)
       (notmuch-logged-error
@@ -549,21 +570,31 @@ options will be handled as specified for
 --batch'. In general we recommend running matching versions of
 the CLI and emacs interface."))
     (goto-char (point-min))
-    (notmuch-remove-if-not
-     #'identity
-     (mapcar
-      (lambda (elem)
-       (let* ((elem-plist (notmuch-hello-saved-search-to-plist elem))
-              (search-query (plist-get elem-plist :query))
-              (filtered-query (notmuch-hello-filtered-query
-                               search-query (plist-get options :filter)))
-              (message-count (prog1 (read (current-buffer))
-                               (forward-line 1))))
-         (when (and filtered-query (or (plist-get options :show-empty-searches)
-                                       (> message-count 0)))
-           (setq elem-plist (plist-put elem-plist :query filtered-query))
-           (plist-put elem-plist :count message-count))))
-      query-list))))
+    (cl-mapcan
+     (lambda (elem)
+       (let* ((elem-plist (notmuch-hello-saved-search-to-plist elem))
+             (search-query (plist-get elem-plist :query))
+             (filtered-query (notmuch-hello-filtered-query
+                              search-query (plist-get options :filter)))
+             (message-count (prog1 (read (current-buffer))
+                              (forward-line 1))))
+        (when (and filtered-query (or (plist-get options :show-empty-searches)
+                                      (> message-count 0)))
+          (setq elem-plist (plist-put elem-plist :query filtered-query))
+          (list (plist-put elem-plist :count message-count)))))
+     query-list)))
+
+(defun notmuch-hello-nice-number (n)
+  (let (result)
+    (while (> n 0)
+      (push (% n 1000) result)
+      (setq n (/ n 1000)))
+    (setq result (or result '(0)))
+    (apply #'concat
+          (number-to-string (car result))
+          (mapcar (lambda (elem)
+                    (format "%s%03d" notmuch-hello-thousands-separator elem))
+                  (cdr result)))))
 
 (defun notmuch-hello-insert-buttons (searches)
   "Insert buttons for SEARCHES.
@@ -589,8 +620,8 @@ with `notmuch-hello-query-counts'."
     (mapc (lambda (elem)
            ;; (not elem) indicates an empty slot in the matrix.
            (when elem
-             (if (> column-indent 0)
-                 (widget-insert (make-string column-indent ? )))
+             (when (> column-indent 0)
+               (widget-insert (make-string column-indent ? )))
              (let* ((name (plist-get elem :name))
                     (query (plist-get elem :query))
                     (oldest-first (cl-case (plist-get elem :sort-order)
@@ -609,7 +640,7 @@ with `notmuch-hello-query-counts'."
                               name)
                (setq column-indent
                      (1+ (max 0 (- column-width (length name)))))))
-           (setq count (1+ count))
+           (cl-incf count)
            (when (eq (% count tags-per-line) 0)
              (setq column-indent 0)
              (widget-insert "\n")))
@@ -619,7 +650,7 @@ with `notmuch-hello-query-counts'."
     (unless (eq (% count tags-per-line) 0)
       (widget-insert "\n"))))
 
-(defimage notmuch-hello-logo ((:type png :file "notmuch-logo.png")))
+;;; Mode
 
 (defun notmuch-hello-update ()
   "Update the notmuch-hello buffer."
@@ -638,7 +669,7 @@ with `notmuch-hello-query-counts'."
     (dolist (window (window-list))
       (let ((last-buf (window-parameter window 'notmuch-hello-last-buffer))
            (cur-buf (window-buffer window)))
-       (when (not (eq last-buf cur-buf))
+       (unless (eq last-buf cur-buf)
          ;; This window changed or is new.  Update recorded buffer
          ;; for next time.
          (set-window-parameter window 'notmuch-hello-last-buffer cur-buf)
@@ -651,46 +682,25 @@ with `notmuch-hello-query-counts'."
       ;; Refresh hello as soon as we get back to redisplay.  On Emacs
       ;; 24, we can't do it right here because something in this
       ;; hook's call stack overrides hello's point placement.
+      ;; FIXME And on Emacs releases that we still support?
       (run-at-time nil nil #'notmuch-hello t))
-    (when (null hello-buf)
+    (unless hello-buf
       ;; Clean up hook
       (remove-hook 'window-configuration-change-hook
                   #'notmuch-hello-window-configuration-change))))
 
-;; the following variable is defined as being defconst in notmuch-version.el
-(defvar notmuch-emacs-version)
-
-(defun notmuch-hello-versions ()
-  "Display the notmuch version(s)."
-  (interactive)
-  (let ((notmuch-cli-version (notmuch-cli-version)))
-    (message "notmuch version %s"
-            (if (string= notmuch-emacs-version notmuch-cli-version)
-                notmuch-cli-version
-              (concat notmuch-cli-version
-                      " (emacs mua version " notmuch-emacs-version ")")))))
-
 (defvar notmuch-hello-mode-map
-  (let ((map (if (fboundp 'make-composed-keymap)
-                ;; Inherit both widget-keymap and
-                ;; notmuch-common-keymap. We have to use
-                ;; make-sparse-keymap to force this to be a new
-                ;; keymap (so that when we modify map it does not
-                ;; modify widget-keymap).
-                (make-composed-keymap (list (make-sparse-keymap) widget-keymap))
-              ;; Before Emacs 24, keymaps didn't support multiple
-              ;; inheritance,, so just copy the widget keymap since
-              ;; it's unlikely to change.
-              (copy-keymap widget-keymap))))
+  ;; Inherit both widget-keymap and notmuch-common-keymap.  We have
+  ;; to use make-sparse-keymap to force this to be a new keymap (so
+  ;; that when we modify map it does not modify widget-keymap).
+  (let ((map (make-composed-keymap (list (make-sparse-keymap) widget-keymap))))
     (set-keymap-parent map notmuch-common-keymap)
-    (define-key map "v" 'notmuch-hello-versions)
     (define-key map (kbd "<C-tab>") 'widget-backward)
     map)
   "Keymap for \"notmuch hello\" buffers.")
-(fset 'notmuch-hello-mode-map notmuch-hello-mode-map)
 
 (define-derived-mode notmuch-hello-mode fundamental-mode "notmuch-hello"
- "Major mode for convenient notmuch navigation. This is your entry portal into notmuch.
 "Major mode for convenient notmuch navigation. This is your entry portal into notmuch.
 
 Saved searches are \"bookmarks\" for arbitrary queries. Hit RET
 or click on a saved search to view matching threads. Edit saved
@@ -720,18 +730,18 @@ The screen may be customized via `\\[customize]'.
 Complete list of currently available key bindings:
 
 \\{notmuch-hello-mode-map}"
(setq notmuch-buffer-refresh-function #'notmuch-hello-update)
- ;;(setq buffer-read-only t)
-)
 (setq notmuch-buffer-refresh-function #'notmuch-hello-update))
+
+;;; Inserters
 
 (defun notmuch-hello-generate-tag-alist (&optional hide-tags)
   "Return an alist from tags to queries to display in the all-tags section."
-  (mapcar (lambda (tag)
-           (cons tag (concat "tag:" (notmuch-escape-boolean-term tag))))
-         (notmuch-remove-if-not
-          (lambda (tag)
-            (not (member tag hide-tags)))
-          (process-lines notmuch-command "search" "--output=tags" "*"))))
+  (cl-mapcan (lambda (tag)
+              (and (not (member tag hide-tags))
+                   (list (cons tag
+                               (concat "tag:"
+                                       (notmuch-escape-boolean-term tag))))))
+            (process-lines notmuch-command "search" "--output=tags" "*")))
 
 (defun notmuch-hello-insert-header ()
   "Insert the default notmuch-hello header."
@@ -757,14 +767,14 @@ Complete list of currently available key bindings:
   (let ((widget-link-prefix "")
        (widget-link-suffix ""))
     (widget-create 'link
-                  :notify (lambda (&rest ignore)
+                  :notify (lambda (&rest _ignore)
                             (browse-url notmuch-hello-url))
                   :help-echo "Visit the notmuch website."
                   "notmuch")
     (widget-insert ". ")
     (widget-insert "You have ")
     (widget-create 'link
-                  :notify (lambda (&rest ignore)
+                  :notify (lambda (&rest _ignore)
                             (notmuch-hello-update))
                   :help-echo "Refresh"
                   (notmuch-hello-nice-number
@@ -783,7 +793,7 @@ Complete list of currently available key bindings:
     (when searches
       (widget-insert "Saved searches: ")
       (widget-create 'push-button
-                    :notify (lambda (&rest ignore)
+                    :notify (lambda (&rest _ignore)
                               (customize-variable 'notmuch-saved-searches))
                     "edit")
       (widget-insert "\n\n")
@@ -799,73 +809,31 @@ Complete list of currently available key bindings:
                 ;; search boxes.
                 :size (max 8 (- (window-width) notmuch-hello-indent
                                 (length "Search: ")))
-                :action (lambda (widget &rest ignore)
-                          (notmuch-hello-search (widget-value widget))))
+                :action #'notmuch-hello-search)
   ;; Add an invisible dot to make `widget-end-of-line' ignore
   ;; trailing spaces in the search widget field.  A dot is used
   ;; instead of a space to make `show-trailing-whitespace'
   ;; happy, i.e. avoid it marking the whole line as trailing
   ;; spaces.
-  (widget-insert ".")
-  (put-text-property (1- (point)) (point) 'invisible t)
+  (widget-insert (propertize "." 'invisible t))
   (widget-insert "\n"))
 
 (defun notmuch-hello-insert-recent-searches ()
   "Insert recent searches."
   (when notmuch-search-history
     (widget-insert "Recent searches: ")
-    (widget-create 'push-button
-                  :notify (lambda (&rest ignore)
-                            (when (y-or-n-p "Are you sure you want to clear the searches? ")
-                              (setq notmuch-search-history nil)
-                              (notmuch-hello-update)))
-                  "clear")
+    (widget-create
+     'push-button
+     :notify (lambda (&rest _ignore)
+              (when (y-or-n-p "Are you sure you want to clear the searches? ")
+                (setq notmuch-search-history nil)
+                (notmuch-hello-update)))
+     "clear")
     (widget-insert "\n\n")
-    (let ((start (point)))
-      (cl-loop for i from 1 to notmuch-hello-recent-searches-max
-              for search in notmuch-search-history do
-              (let ((widget-symbol (intern (format "notmuch-hello-search-%d" i))))
-                (set widget-symbol
-                     (widget-create 'editable-field
-                                    ;; Don't let the search boxes be
-                                    ;; less than 8 characters wide.
-                                    :size (max 8
-                                               (- (window-width)
-                                                  ;; Leave some space
-                                                  ;; at the start and
-                                                  ;; end of the
-                                                  ;; boxes.
-                                                  (* 2 notmuch-hello-indent)
-                                                  ;; 1 for the space
-                                                  ;; before the
-                                                  ;; `[save]' button. 6
-                                                  ;; for the `[save]'
-                                                  ;; button.
-                                                  1 6
-                                                  ;; 1 for the space
-                                                  ;; before the `[del]'
-                                                  ;; button. 5 for the
-                                                  ;; `[del]' button.
-                                                  1 5))
-                                    :action (lambda (widget &rest ignore)
-                                              (notmuch-hello-search (widget-value widget)))
-                                    search))
-                (widget-insert " ")
-                (widget-create 'push-button
-                               :notify (lambda (widget &rest ignore)
-                                         (notmuch-hello-add-saved-search widget))
-                               :notmuch-saved-search-widget widget-symbol
-                               "save")
-                (widget-insert " ")
-                (widget-create 'push-button
-                               :notify (lambda (widget &rest ignore)
-                                         (when (y-or-n-p "Are you sure you want to delete this search? ")
-                                           (notmuch-hello-delete-search-from-history widget)))
-                               :notmuch-saved-search-widget widget-symbol
-                               "del"))
-              (widget-insert "\n"))
-      (indent-rigidly start (point) notmuch-hello-indent))
-    nil))
+    (let ((width (notmuch-search-item-field-width)))
+      (dolist (search (seq-take notmuch-search-history
+                               notmuch-hello-recent-searches-max))
+       (widget-create 'notmuch-search-item :value search :size width)))))
 
 (defun notmuch-hello-insert-searches (title query-list &rest options)
   "Insert a section with TITLE showing a list of buttons made from QUERY-LIST.
@@ -890,25 +858,25 @@ Supports the following entries in OPTIONS as a plist:
    the same values as :filter. If :filter and :filter-count are specified, this
    will be used instead of :filter, not in conjunction with it."
   (widget-insert title ": ")
-  (if (and notmuch-hello-first-run (plist-get options :initially-hidden))
-      (add-to-list 'notmuch-hello-hidden-sections title))
+  (when (and notmuch-hello-first-run (plist-get options :initially-hidden))
+    (add-to-list 'notmuch-hello-hidden-sections title))
   (let ((is-hidden (member title notmuch-hello-hidden-sections))
        (start (point)))
     (if is-hidden
        (widget-create 'push-button
-                      :notify `(lambda (widget &rest ignore)
+                      :notify `(lambda (widget &rest _ignore)
                                  (setq notmuch-hello-hidden-sections
                                        (delete ,title notmuch-hello-hidden-sections))
                                  (notmuch-hello-update))
                       "show")
       (widget-create 'push-button
-                    :notify `(lambda (widget &rest ignore)
+                    :notify `(lambda (widget &rest _ignore)
                                (add-to-list 'notmuch-hello-hidden-sections
                                             ,title)
                                (notmuch-hello-update))
                     "hide"))
     (widget-insert "\n")
-    (when (not is-hidden)
+    (unless is-hidden
       (let ((searches (apply 'notmuch-hello-query-counts query-list options)))
        (when (or (not (plist-get options :hide-if-empty))
                  searches)
@@ -951,19 +919,21 @@ following:
     (widget-insert "Hit `?' for context-sensitive help in any Notmuch screen.\n")
     (widget-insert "Customize ")
     (widget-create 'link
-                  :notify (lambda (&rest ignore)
+                  :notify (lambda (&rest _ignore)
                             (customize-group 'notmuch))
                   :button-prefix "" :button-suffix ""
                   "Notmuch")
     (widget-insert " or ")
     (widget-create 'link
-                  :notify (lambda (&rest ignore)
+                  :notify (lambda (&rest _ignore)
                             (customize-variable 'notmuch-hello-sections))
                   :button-prefix "" :button-suffix ""
                   "this page.")
     (let ((fill-column (- (window-width) notmuch-hello-indent)))
       (center-region start (point)))))
 
+;;; Hello!
+
 ;;;###autoload
 (defun notmuch-hello (&optional no-display)
   "Run notmuch and display saved searches, known tags, etc."
@@ -974,7 +944,7 @@ following:
   (let ((notmuch-hello-auto-refresh nil))
     (if no-display
        (set-buffer "*notmuch-hello*")
-      (switch-to-buffer "*notmuch-hello*")))
+      (pop-to-buffer-same-window "*notmuch-hello*")))
   ;; Install auto-refresh hook
   (when notmuch-hello-auto-refresh
     (add-hook 'window-configuration-change-hook
@@ -1015,12 +985,7 @@ following:
   (run-hooks 'notmuch-hello-refresh-hook)
   (setq notmuch-hello-first-run nil))
 
-(defun notmuch-folder ()
-  "Deprecated function for invoking notmuch---calling `notmuch' is preferred now."
-  (interactive)
-  (notmuch-hello))
-
-;;
+;;; _
 
 (provide 'notmuch-hello)