Function: logview--initialize-submode

logview--initialize-submode is a natively compiled function defined in logview.el.

Signature

(logview--initialize-submode NAME DEFINITION STANDARD-TIMESTAMPS &optional TEST-LINE)

Source Code

;; Defined in /nix/store/3zkn9hgpv447zb8nrg55d9x2ynmmrk40-emacs-packages-deps/share/emacs/site-lisp/elpa/logview-20260218.2013/logview.el
;; Returns non-nil if TEST-LINE is "promising".
(defun logview--initialize-submode (name definition standard-timestamps &optional test-line)
  (let* ((format            (cdr (assq 'format    definition)))
         (timestamp-names   (when test-line (cdr (assq 'timestamp definition))))
         (timestamp-options (if timestamp-names
                                (mapcar (lambda (name)
                                          (logview--get-split-alists name "timestamp format"
                                                                     logview-additional-timestamp-formats logview-std-timestamp-formats))
                                        ;; Don't be too strict to the definition.  Many if not most users
                                        ;; don't go through customization interface to create it.
                                        (if (listp timestamp-names) timestamp-names (list timestamp-names)))
                              standard-timestamps))
         (search-from       0)
         (parts             (list "^"))
         next
         end
         starter terminator
         levels
         timestamp-at
         cannot-match
         features
         have-explicit-message
         (add-text-part (lambda (from to)
                          (push (replace-regexp-in-string "[ \t]+" "[ \t]+" (regexp-quote (substring format from to))) parts))))
    (unless (and (stringp format) (> (length format) 0))
      (user-error "Invalid submode '%s': no format string" name))
    (while (setq next (string-match logview--entry-part-regexp format search-from))
      (when (> next search-from)
        (funcall add-text-part search-from next))
      (setq end        (match-end 0)
            starter    (when (> next 0)
                         (aref format (1- next)))
            terminator (when (< end (length format))
                         (aref format end)))
      (cond ((match-beginning logview--timestamp-group)
             (push nil parts)
             (push 'timestamp features)
             (setq timestamp-at parts))
            ((match-beginning logview--level-group)
             (setq levels (logview--get-split-alists (cdr (assq 'levels definition)) "level mapping"
                                                     logview-additional-level-mappings logview-std-level-mappings))
             (push (format "\\(?%d:%s\\)" logview--level-group
                           (regexp-opt (apply #'append (mapcar (lambda (final-level) (cdr (assq final-level levels)))
                                                               logview--final-levels))))
                   parts)
             (push 'level features))
            ((match-beginning logview--message-group)
             (unless (= (match-end logview--message-group) (length format))
               (user-error "Field `MESSAGE' can only be placed at the very end of format string"))
             (setq have-explicit-message t))
            (t
             (dolist (k (list logview--name-group logview--thread-group logview--ignored-group))
               ;; See definition of `logview--entry-part-regexp' for the meaning of 4 and 10.
               (let ((special-regexp (match-beginning (+ k 4))))
                 (when (or (match-beginning k) special-regexp)
                   (push (format "\\(?%s:%s\\)"
                                 (if (/= k logview--ignored-group)
                                     (number-to-string k)
                                   "")
                                 (cond (special-regexp
                                        (let ((forced-regexp (match-string 10 format)))
                                          (unless (logview--valid-regexp-p forced-regexp)
                                            ;; Ideally would also ensure that there are no catching groups,
                                            ;; but for this we'd need `xr' as dependency.  Not now.
                                            (warn "In format specifier `%s': `%s' is not a valid regexp" format forced-regexp)
                                            (setf cannot-match t))
                                          forced-regexp))
                                       ((and starter terminator
                                             (or (and (= starter ?\() (= terminator ?\)))
                                                 (and (= starter ?\[) (= terminator ?\]))))
                                        ;; See https://github.com/doublep/logview/issues/2 We allow _one_
                                        ;; level of nested parens inside parenthesized THREAD or NAME.
                                        ;; Allowing more would complicate regexp even further.  Unlimited
                                        ;; nesting level is not possible with regexps at all.
                                        ;;
                                        ;; 'rx-to-string' is used to avoid escaping things ourselves.
                                        (rx-to-string `(seq (* (not (any ,starter ,terminator ?\n)))
                                                            (* ,starter (* (not (any ?\n))) ,terminator
                                                               (* (not (any ,starter ,terminator ?\n)))))
                                                      t))
                                       ((and terminator (/= terminator ? ))
                                        (format "[^%c\n]*" terminator))
                                       (terminator
                                        "[^ \t\n]+")
                                       (t
                                        ".+")))
                              parts)
                        (push (if (= k logview--name-group) 'name 'thread) features))))))
      (setq search-from end))
    (unless cannot-match
      (when (< search-from (length format))
        (funcall add-text-part search-from nil))
      ;; Unless `MESSAGE' field is used explicitly, behave as if format string ends with whitespace.
      (unless (or have-explicit-message (string-match-p "[ \t]$" format))
        (push "\\(?:[ \t]+\\|$\\)" parts))
      (setq parts (nreverse parts))
      (when timestamp-at
        ;; Speed optimization: if the submode includes a timestamp, but the test line doesn't have even two
        ;; digits at the expected place, don't even loop through all the timestamp options.
        (setcar timestamp-at ".*[0-9][0-9].*")
        (when (and test-line (not (string-match-p (apply #'concat parts) test-line)))
          (setq cannot-match t))))
    (unless cannot-match
      (dolist (timestamp-option (if timestamp-at timestamp-options '(nil)))
        (let* ((timestamp-pattern (assq 'java-pattern timestamp-option))
               (timestamp-locale  (cdr (assq 'locale timestamp-option)))
               (timestamp-regexp  (if timestamp-pattern
                                      (condition-case error
                                          (apply #'datetime-matching-regexp 'java (cdr timestamp-pattern)
                                                 :locale timestamp-locale
                                                 (append (cdr (assq 'datetime-options timestamp-option)) logview--datetime-matching-options))
                                        ;; 'datetime' doesn't mention the erroneous pattern to keep
                                        ;; the error message concise.  Let's do it ourselves.
                                        (error (warn "In Java timestamp pattern '%s': %s"
                                                     (cdr timestamp-pattern) (error-message-string error))
                                               nil))
                                    (cdr (assq 'regexp timestamp-option)))))
          (when (or timestamp-regexp (null timestamp-at))
            (when timestamp-at
              (setcar timestamp-at (format "\\(?%d:%s\\)" logview--timestamp-group timestamp-regexp)))
            (let ((regexp      (apply #'concat parts))
                  (level-index 0))
              (when (or (null test-line) (string-match-p regexp test-line))
                (setq logview--submode-name           name
                      logview--process-buffer-changes t
                      logview--entry-regexp           regexp
                      logview--submode-features       features
                      logview--submode-level-data     nil)
                (logview--update-mode-name)
                (when (memq 'level features)
                  (dolist (final-level logview--final-levels)
                    (dolist (level (cdr (assoc final-level levels)))
                      (push (cons level (cons level-index (cons (intern (format "logview-%s-entry" (symbol-name final-level)))
                                                                (intern (format "logview-level-%s" (symbol-name final-level))))))
                            logview--submode-level-data)
                      (setq level-index (1+ level-index)))))
                (setq logview--submode-level-faces (make-vector level-index nil))
                (dolist (level-data logview--submode-level-data)
                  (aset logview--submode-level-faces (cadr level-data) (cddr level-data)))
                (when (memq 'timestamp features)
                  (let ((num-fractionals (apply #'datetime-pattern-num-second-fractionals 'java (cdr timestamp-pattern) logview--datetime-parsing-options)))
                    (setf logview--timestamp-difference-format-string (format "%%+.%df" num-fractionals)
                          logview--timestamp-gap-format-string        (format "%%.%df" num-fractionals)))
                  ;; Largely for catching errors in `datetime's determination of system timezone.
                  (setf logview--submode-timestamp-parser
                        (condition-case error
                            (apply #'datetime-parser-to-float 'java (cdr timestamp-pattern) :locale timestamp-locale :timezone 'system
                                   logview--datetime-parsing-options)
                          (error (warn "%s" (error-message-string error))
                                 (let ((utc-parser (ignore-errors (apply #'datetime-parser-to-float 'java (cdr timestamp-pattern) :locale timestamp-locale
                                                                         logview--datetime-parsing-options))))
                                   (if utc-parser
                                       (progn (warn "Using UTC for the log file instead, in hopes it will be good enough")
                                              utc-parser)
                                     ;; Only to avoid errors later.  Results will be incorrect, of course, but
                                     ;; at least the mode everything other than some timestamp-related
                                     ;; commands will work.  The cause is reported as a warning above.
                                     (lambda (_) 0.0)))))))
                (read-only-mode 1)
                (when buffer-file-name
                  (pcase logview-auto-revert-mode
                    (`auto-revert-mode      (auto-revert-mode      1))
                    (`auto-revert-tail-mode (auto-revert-tail-mode 1))))
                (logview--refilter)
                (throw 'success nil))))))
      ;; "Promising" line.
      t)))