emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[nongnu] elpa/subed ea143d6f63: Drop unneccessary match-data saves and c


From: ELPA Syncer
Subject: [nongnu] elpa/subed ea143d6f63: Drop unneccessary match-data saves and checks for batch-editing
Date: Sun, 13 Feb 2022 09:58:44 -0500 (EST)

branch: elpa/subed
commit ea143d6f6373fe7fcd16d19d5f2d2685a85afd57
Author: Sacha Chua <sacha@sachachua.com>
Commit: Sacha Chua <sacha@sachachua.com>

    Drop unneccessary match-data saves and checks for batch-editing
    
    * subed/subed-common.el (subed-batch-edit): Add somewhat paranoid check
      for subed--batch-editing in case we're in a nested call.
      (subed--buffer-chars-modified-tick): New internal variable.
      (subed--post-command-handler): Check buffer-chars-modified-tick.
      (subed-disable-sync-player-to-point): Remove subed--batch-editing
      check because this should only be called within post-command-hook.
      (subed--sync-player-to-point): Remove subed--batch-editing
      check because this should only be called within post-command-hook.
      (subed-show-cps-p, subed--move-cps-overlay-to-current-subtitle,
      subed-enable-show-cps, subed-disable-show-cps): Move to
      post-command-hook instead of after-change-functions.
      (subed-pause-while-typing-p, subed-enable-pause-while-typing,
      subed-disable-pause-while-typing): Move to post-command-hook instead
      of after-change-functions.
      (subed--move-cps-overlay-to-current-subtitle,
      subed--update-cps-overlay): Check overlay more efficiently.
      (subed--identify-overlaps): Don't save match data unnecessarily
      (set-subtitle-time-start, set-subtitle-time-stop): Save match data before 
replacing.
    * subed/subed-vtt.el (subed--timestamp-to-msecs,
      subed--msecs-to-timestamp, subed--subtitle-id,
      subed--jump-to-subtitle-id, subed--jump-to-subtitle-time-start,
      subed--jump-to-subtitle-time-stop, subed--jump-to-subtitle-end,
      subed--forward-subtitle-id, subed--backward-subtitle-id,
      subed--prepend-subtitle, subed--append-subtitle,
      subed--sanitize-format, subed--validate-format): Don't save match data
      unnecessarily
      (subed-vtt--regexp-identifier): Wrap long lines.
      (syntax-propertize-function): Add.
    * subed/subed-ass.el (subed--timestamp-to-msecs,
      subed--msecs-to-timestamp, subed--subtitle-id-at-msecs,
      subed--jump-to-subtitle-id, subed--jump-to-subtitle-time-start,
      subed--jump-to-subtitle-time-stop, subed--jump-to-subtitle-end,
      subed--forward-subtitle-id): Don't save match data unnecessarily
    * subed/subed-srt.el (subed--timestamp-to-msecs,
      subed--msecs-to-timestamp, subed--subtitle-id-at-msecs,
      subed--jump-to-subtitle-id, subed--jump-to-subtitle-time-start,
      subed--jump-to-subtitle-time-stop, subed--jump-to-subtitle-end,
      subed--forward-subtitle-id, subed--prepend-subtitle,
      subed--append-subtitle, subed--merge-with-next,
      subed--sanitize-format, subed--validate-format): Don't save match
      data unnecessarily
    
    Co-authored-by: Stefan Monnier
---
 subed/subed-ass.el    | 158 ++++++++++++------------
 subed/subed-common.el |  95 ++++++++-------
 subed/subed-srt.el    | 320 ++++++++++++++++++++++++-------------------------
 subed/subed-vtt.el    | 324 +++++++++++++++++++++++++-------------------------
 4 files changed, 442 insertions(+), 455 deletions(-)

diff --git a/subed/subed-ass.el b/subed/subed-ass.el
index 15b4e9e5b6..ba2be08955 100644
--- a/subed/subed-ass.el
+++ b/subed/subed-ass.el
@@ -52,23 +52,20 @@
   "Find HH:MM:SS.MS pattern in TIME-STRING and convert it to milliseconds.
 Return nil if TIME-STRING doesn't match the pattern.
 Use the format-specific function for MAJOR-MODE."
-  (save-match-data
-    (when (string-match subed--regexp-timestamp time-string)
-      (let ((hours (string-to-number (or (match-string 2 time-string) "0")))
-            (mins  (string-to-number (match-string 3 time-string)))
-            (secs  (string-to-number (match-string 4 time-string)))
-            (msecs (if (match-string 6 time-string) (string-to-number 
(subed--right-pad (match-string 6 time-string) 3 ?0)) 0)))
-        (+ (* (truncate hours) 3600000)
-           (* (truncate mins) 60000)
-           (* (truncate secs) 1000)
-           (truncate msecs))))))
+  (when (string-match subed--regexp-timestamp time-string)
+    (let ((hours (string-to-number (or (match-string 2 time-string) "0")))
+          (mins  (string-to-number (match-string 3 time-string)))
+          (secs  (string-to-number (match-string 4 time-string)))
+          (msecs (if (match-string 6 time-string) (string-to-number 
(subed--right-pad (match-string 6 time-string) 3 ?0)) 0)))
+      (+ (* (truncate hours) 3600000)
+         (* (truncate mins) 60000)
+         (* (truncate secs) 1000)
+         (truncate msecs)))))
 
 (cl-defmethod subed--msecs-to-timestamp (msecs &context (major-mode 
subed-ass-mode))
   "Convert MSECS to string in the format H:MM:SS.CS.
 Use the format-specific function for MAJOR-MODE."
-  ;; We need to wrap format-seconds in save-match-data because it does regexp
-  ;; stuff and we need to preserve our own match-data.
-  (concat (save-match-data (format-seconds "%h:%02m:%02s" (/ msecs 1000)))
+  (concat (format-seconds "%h:%02m:%02s" (/ msecs 1000))
           "." (format "%02d" (/ (mod msecs 1000) 10))))
 
 (cl-defmethod subed--subtitle-id (&context (major-mode subed-ass-mode))
@@ -83,26 +80,25 @@ Use the format-specific function for MAJOR-MODE."
   "Return the ID of the subtitle at MSECS milliseconds.
 Return nil if there is no subtitle at MSECS.
 Use the format-specific function for MAJOR-MODE."
-  (save-match-data
-    (save-excursion
-      (goto-char (point-min))
-      (let* ((secs       (/ msecs 1000))
-             (only-hours (truncate (/ secs 3600)))
-             (only-mins  (truncate (/ (- secs (* only-hours 3600)) 60))))
-        ;; Move to first subtitle in the relevant hour
-        (when (re-search-forward (format "\\(%s\\|\\`\\)%02d:" 
subed--regexp-separator only-hours) nil t)
-          (beginning-of-line)
-          ;; Move to first subtitle in the relevant hour and minute
-          (re-search-forward (format "\\(\n\n\\|\\`\\)%02d:%02d" only-hours 
only-mins) nil t)))
-      ;; Move to first subtitle that starts at or after MSECS
-      (catch 'subtitle-id
-        (while (<= (or (subed-subtitle-msecs-start) -1) msecs)
-          ;; If stop time is >= MSECS, we found a match
-          (let ((cur-sub-end (subed-subtitle-msecs-stop)))
-            (when (and cur-sub-end (>= cur-sub-end msecs))
-              (throw 'subtitle-id (subed-subtitle-id))))
-          (unless (subed--forward-subtitle-id)
-            (throw 'subtitle-id nil)))))))
+  (save-excursion
+    (goto-char (point-min))
+    (let* ((secs       (/ msecs 1000))
+           (only-hours (truncate (/ secs 3600)))
+           (only-mins  (truncate (/ (- secs (* only-hours 3600)) 60))))
+      ;; Move to first subtitle in the relevant hour
+      (when (re-search-forward (format "\\(%s\\|\\`\\)%02d:" 
subed--regexp-separator only-hours) nil t)
+        (beginning-of-line)
+        ;; Move to first subtitle in the relevant hour and minute
+        (re-search-forward (format "\\(\n\n\\|\\`\\)%02d:%02d" only-hours 
only-mins) nil t)))
+    ;; Move to first subtitle that starts at or after MSECS
+    (catch 'subtitle-id
+      (while (<= (or (subed-subtitle-msecs-start) -1) msecs)
+        ;; If stop time is >= MSECS, we found a match
+        (let ((cur-sub-end (subed-subtitle-msecs-stop)))
+          (when (and cur-sub-end (>= cur-sub-end msecs))
+            (throw 'subtitle-id (subed-subtitle-id))))
+        (unless (subed--forward-subtitle-id)
+          (throw 'subtitle-id nil))))))
 
 ;;; Traversing
 
@@ -112,51 +108,51 @@ If SUB-ID is not given, focus the current subtitle's ID.
 Return point or nil if no subtitle ID could be found.
 ASS doesn't use IDs, so we use the starting timestamp instead.
 Use the format-specific function for MAJOR-MODE."
-  (save-match-data
-    (if (stringp sub-id)
-        (let* ((orig-point (point))
-               (find-ms (subed--timestamp-to-msecs sub-id))
-               (regex (concat "^\\(?:" subed-ass--regexp-start "\\)\\(" 
subed--regexp-timestamp "\\)"))
-               done)
-          (goto-char (point-min))
-          (while (not done)
-            (if (re-search-forward regex nil t)
-                (when (= (subed-timestamp-to-msecs (match-string 1)) find-ms)
-                  (setq done 'found)
-                  (goto-char (match-beginning 1)))
-              (setq done 'not-found)
-              (goto-char orig-point)))
-          (when (eq done 'found)
-            (beginning-of-line)
-            (point)))
-      (end-of-line)
-      (let* ((regex (concat "^\\(?:" subed-ass--regexp-start "\\)\\(" 
subed--regexp-timestamp "\\)"))
-             (match-found (re-search-backward regex nil t)))
-        (when (or match-found (re-search-forward regex nil t)) ;; maybe at the 
beginning?
-          (goto-char (match-beginning 0))
-          (point))))))
+  (if (stringp sub-id)
+      (let* ((orig-point (point))
+             (find-ms (subed--timestamp-to-msecs sub-id))
+             (regex (concat "^\\(?:" subed-ass--regexp-start "\\)\\(" 
subed--regexp-timestamp "\\)"))
+             done)
+        (goto-char (point-min))
+        (while (not done)
+          (if (re-search-forward regex nil t)
+              (when (= (save-match-data
+                         (subed-timestamp-to-msecs
+                          (match-string 1)))
+                       find-ms)
+                (setq done 'found)
+                (goto-char (match-beginning 1)))
+            (setq done 'not-found)
+            (goto-char orig-point)))
+        (when (eq done 'found)
+          (beginning-of-line)
+          (point)))
+    (end-of-line)
+    (let* ((regex (concat "^\\(?:" subed-ass--regexp-start "\\)\\(" 
subed--regexp-timestamp "\\)"))
+           (match-found (re-search-backward regex nil t)))
+      (when (or match-found (re-search-forward regex nil t)) ;; maybe at the 
beginning?
+        (goto-char (match-beginning 0))
+        (point)))))
 
 (cl-defmethod subed--jump-to-subtitle-time-start (&context (major-mode 
subed-ass-mode) &optional sub-id)
   "Move point to subtitle's start time.
 If SUB-ID is not given, use subtitle on point.
 Return point or nil if no start time could be found.
 Use the format-specific function for MAJOR-MODE."
-  (save-match-data
-    (when (subed-jump-to-subtitle-id sub-id)
-      (when (re-search-forward subed--regexp-timestamp (line-end-position) t)
-        (goto-char (match-beginning 0))
-        (point)))))
+  (when (subed-jump-to-subtitle-id sub-id)
+    (when (re-search-forward subed--regexp-timestamp (line-end-position) t)
+      (goto-char (match-beginning 0))
+      (point))))
 
 (cl-defmethod subed--jump-to-subtitle-time-stop (&context (major-mode 
subed-ass-mode) &optional sub-id)
   "Move point to subtitle's stop time.
 If SUB-ID is not given, use subtitle on point.  Return point or
 nil if no stop time could be found.  Use the format-specific
 function for MAJOR-MODE."
-  (save-match-data
-    (when (subed-jump-to-subtitle-id sub-id)
-      (re-search-forward (concat "\\(?:" subed--regexp-timestamp "\\),") 
(point-at-eol) t)
-      (when (looking-at subed--regexp-timestamp)
-        (point)))))
+  (when (subed-jump-to-subtitle-id sub-id)
+    (re-search-forward (concat "\\(?:" subed--regexp-timestamp "\\),") 
(point-at-eol) t)
+    (when (looking-at subed--regexp-timestamp)
+      (point))))
 
 (cl-defmethod subed--jump-to-subtitle-text (&context (major-mode 
subed-ass-mode) &optional sub-id)
   "Move point on the first character of subtitle's text.
@@ -174,27 +170,25 @@ format-specific function for MAJOR-MODE."
 If SUB-ID is not given, use subtitle on point.  Return point or
 nil if point did not change or if no subtitle end can be found.
 Use the format-specific function for MAJOR-MODE."
-  (save-match-data
-    (let ((orig-point (point)))
-      (when (subed-jump-to-subtitle-text sub-id)
-        (end-of-line)
-        (unless (= orig-point (point))
-          (point))))))
+  (let ((orig-point (point)))
+    (when (subed-jump-to-subtitle-text sub-id)
+      (end-of-line)
+      (unless (= orig-point (point))
+        (point)))))
 
 (cl-defmethod subed--forward-subtitle-id (&context (major-mode subed-ass-mode))
   "Move point to next subtitle's ID.
 Return point or nil if there is no next subtitle.  Use the
 format-specific function for MAJOR-MODE."
-  (save-match-data
-    (let ((pos (point)))
-      (forward-line 1)
-      (beginning-of-line)
-      (while (not (or (eobp) (looking-at subed-ass--regexp-start)))
-        (forward-line 1))
-      (if (looking-at subed-ass--regexp-start)
-          (point)
-        (goto-char pos)
-        nil))))
+  (let ((pos (point)))
+    (forward-line 1)
+    (beginning-of-line)
+    (while (not (or (eobp) (looking-at subed-ass--regexp-start)))
+      (forward-line 1))
+    (if (looking-at subed-ass--regexp-start)
+        (point)
+      (goto-char pos)
+      nil)))
 
 (cl-defmethod subed--backward-subtitle-id (&context (major-mode 
subed-ass-mode))
   "Move point to previous subtitle's ID.
diff --git a/subed/subed-common.el b/subed/subed-common.el
index cbe08c47ee..0e6c2c040e 100644
--- a/subed/subed-common.el
+++ b/subed/subed-common.el
@@ -259,7 +259,8 @@ Return the new subtitle start time in milliseconds."
               (and sub-id (subed-jump-to-subtitle-id sub-id)))
       (when (and (subed-jump-to-subtitle-time-start sub-id)
                  (looking-at subed--regexp-timestamp))
-        (replace-match (subed-msecs-to-timestamp msecs))))))
+        (replace-match
+         (save-match-data (subed-msecs-to-timestamp msecs)))))))
 
 (subed-define-generic-function set-subtitle-time-stop (msecs &optional sub-id)
   "Set subtitle stop time to MSECS milliseconds.
@@ -270,7 +271,8 @@ Return the new subtitle stop time in milliseconds."
   (save-excursion
     (when (and (subed-jump-to-subtitle-time-stop sub-id)
                (looking-at subed--regexp-timestamp))
-      (replace-match (subed-msecs-to-timestamp msecs)))))
+      (replace-match
+       (save-match-data (subed-msecs-to-timestamp msecs))))))
 
 (subed-define-generic-function make-subtitle (&optional id start stop text)
   "Generate new subtitle string.
@@ -453,9 +455,12 @@ Before BODY is run, point is placed on the subtitle's ID."
        (subed-with-subtitle-replay-disabled
          (subed-disable-sync-point-to-player-temporarily)
          (progn ,@body)))
-     (when (subed-show-cps-p)
-       (subed--move-cps-overlay-to-current-subtitle)
-       (subed--update-cps-overlay))))
+     (unless subed--batch-editing
+       ;; I wonder if we should do this here or if we should rely on
+       ;; it being in post-command-hook...
+       (when (subed-show-cps-p)
+         (subed--move-cps-overlay-to-current-subtitle)
+         (subed--update-cps-overlay)))))
 
 (defun subed--right-pad (string length fillchar)
   "Use FILLCHAR to make STRING LENGTH characters long."
@@ -465,23 +470,26 @@ Before BODY is run, point is placed on the subtitle's ID."
 ;;; Hooks for point motion and subtitle motion
 
 (defvar-local subed--current-point -1)
+(defvar-local subed--buffer-chars-modified-tick -1)
 (defvar-local subed--current-subtitle-id -1)
 (defun subed--post-command-handler ()
   "Detect point motion and user entering text and signal hooks."
   ;; Check for point motion first to avoid expensive calls to subed-subtitle-id
   ;; as often as possible.
-  (let ((new-point (point)))
+  (let ((new-point (point))
+        (new-buffer-chars-modified-tick (buffer-chars-modified-tick)))
     (when (and
-           (not subed--batch-editing)
            new-point subed--current-point
-           (not (= new-point subed--current-point)))
+           (or (not (= new-point subed--current-point))
+               (not (= new-buffer-chars-modified-tick 
subed--buffer-chars-modified-tick))))
 
       ;; If point is synced to playback position, temporarily disable that so
       ;; that manual moves aren't cancelled immediately by automated moves.
       (subed-disable-sync-point-to-player-temporarily)
 
       ;; Store new point and fire signal.
-      (setq subed--current-point new-point)
+      (setq subed--current-point new-point
+            subed--buffer-chars-modified-tick new-buffer-chars-modified-tick)
       (run-hooks 'subed-point-motion-hook)
 
       ;; Check if point moved across subtitle boundaries.
@@ -1260,7 +1268,6 @@ If QUIET is non-nil, do not display a message in the 
minibuffer."
 (defun subed--sync-point-to-player (msecs)
   "Move point to subtitle at MSECS."
   (when (and (not (use-region-p)) ;; Don't sync with active-mark in 
transient-mark-mode
-             (not subed--batch-editing)
              (subed-jump-to-subtitle-text-at-msecs msecs))
     (subed-debug "Synchronized point to playback position: %s -> #%s"
                  (subed-msecs-to-timestamp msecs) (subed-subtitle-id))
@@ -1330,16 +1337,15 @@ If QUIET is non-nil, do not display a message in the 
minibuffer."
 
 (defun subed--sync-player-to-point ()
   "Seek player to currently focused subtitle."
-  (unless subed--batch-editing
-    (subed-debug "Seeking player to subtitle at point %s" (point))
-    (let ((cur-sub-start (subed-subtitle-msecs-start))
-          (cur-sub-stop (subed-subtitle-msecs-stop)))
-      (when (and subed-mpv-playback-position cur-sub-start cur-sub-stop
-                 (or (< subed-mpv-playback-position cur-sub-start)
-                     (> subed-mpv-playback-position cur-sub-stop)))
-        (subed-mpv-jump cur-sub-start)
-        (subed-debug "Synchronized playback position to point: #%s -> %s"
-                     (subed-subtitle-id) cur-sub-start)))))
+  (subed-debug "Seeking player to subtitle at point %s" (point))
+  (let ((cur-sub-start (subed-subtitle-msecs-start))
+        (cur-sub-stop (subed-subtitle-msecs-stop)))
+    (when (and subed-mpv-playback-position cur-sub-start cur-sub-stop
+               (or (< subed-mpv-playback-position cur-sub-start)
+                   (> subed-mpv-playback-position cur-sub-stop)))
+      (subed-mpv-jump cur-sub-start)
+      (subed-debug "Synchronized playback position to point: #%s -> %s"
+                   (subed-subtitle-id) cur-sub-start))))
 
 
 ;;; Loop over single subtitle
@@ -1430,7 +1436,7 @@ If QUIET is non-nil, do not display a message in the 
minibuffer."
 
 See `subed-playback-speed-while-typing' and
 `subed-playback-speed-while-not-typing'."
-  (member #'subed--pause-while-typing after-change-functions))
+  (member #'subed--pause-while-typing post-command-hook))
 
 (defun subed-enable-pause-while-typing (&optional quiet)
   "Pause player while the user is editing a subtitle.
@@ -1440,7 +1446,7 @@ resumed automatically unless the player was paused 
already.
 
 If QUIET is non-nil, do not display a message in the minibuffer."
   (unless (subed-pause-while-typing-p)
-    (add-hook 'after-change-functions #'subed--pause-while-typing :append 
:local)
+    (add-hook 'post-command-hook #'subed--pause-while-typing :append :local)
     (unless quiet
       (subed-debug "%S" subed-playback-speed-while-typing)
       (if (<= subed-playback-speed-while-typing 0)
@@ -1453,7 +1459,7 @@ If QUIET is non-nil, do not display a message in the 
minibuffer."
 
 If QUIET is non-nil, do not display a message in the minibuffer."
   (when (subed-pause-while-typing-p)
-    (remove-hook 'after-change-functions #'subed--pause-while-typing :local)
+    (remove-hook 'post-command-hook #'subed--pause-while-typing :local)
     (unless quiet
       (message "Playback speed will not change while subtitle texts are 
edited"))))
 
@@ -1567,12 +1573,13 @@ attribute(s)."
 
 (defun subed-show-cps-p ()
   "Whether CPS is shown for the current subtitle."
-  (member #'subed--update-cps-overlay after-change-functions))
+  (member #'subed--update-cps-overlay post-command-hook))
 
 (defun subed-enable-show-cps (&optional quiet)
   "Enable showing CPS next to the subtitle heading."
   (interactive "p")
-  (add-hook 'after-change-functions #'subed--update-cps-overlay nil t)
+  ;; FIXME: Consider displaying CPS on all cues (via jit-lock) rather than the 
current one?
+  (add-hook 'post-command-hook #'subed--update-cps-overlay nil t)
   (add-hook 'subed-subtitle-motion-hook 
#'subed--move-cps-overlay-to-current-subtitle nil t)
   (add-hook 'after-save-hook #'subed--move-cps-overlay-to-current-subtitle nil 
t)
   (unless quiet
@@ -1581,7 +1588,7 @@ attribute(s)."
 (defun subed-disable-show-cps (&optional quiet)
   "Disable showing CPS next to the subtitle heading."
   (interactive)
-  (remove-hook 'after-change-functions #'subed--update-cps-overlay t)
+  (remove-hook 'post-command-hook #'subed--update-cps-overlay t)
   (remove-hook 'subed-subtitle-motion-hook 
#'subed--move-cps-overlay-to-current-subtitle t)
   (remove-hook 'after-save-hook #'subed--move-cps-overlay-to-current-subtitle 
t)
   (unless quiet
@@ -1608,19 +1615,18 @@ attribute(s)."
 (defun subed-calculate-cps (&optional print-message)
   "Calculate characters per second of the current subtitle."
   (interactive "p")
-  (save-match-data
-    (let* ((msecs-start (ignore-errors (subed-subtitle-msecs-start)))
-          (msecs-stop (ignore-errors (subed-subtitle-msecs-stop)))
-          (text (if (fboundp subed-transform-for-cps)
-                    (funcall subed-transform-for-cps (subed-subtitle-text))
-                  (subed-subtitle-text)))
-          (length (length text))
-          (cps (when (and (numberp msecs-stop)
-                          (numberp msecs-start))
-                 (/ length 0.001 (- msecs-stop msecs-start)))))
-      (if (and print-message cps)
-         (message "%.1f characters per second" cps)
-       cps))))
+  (let* ((msecs-start (ignore-errors (subed-subtitle-msecs-start)))
+              (msecs-stop (ignore-errors (subed-subtitle-msecs-stop)))
+              (text (if (fboundp subed-transform-for-cps)
+                              (funcall subed-transform-for-cps 
(subed-subtitle-text))
+                            (subed-subtitle-text)))
+              (length (length text))
+              (cps (when (and (numberp msecs-stop)
+                                          (numberp msecs-start))
+                           (/ length 0.001 (- msecs-stop msecs-start)))))
+    (if (and print-message cps)
+             (message "%.1f characters per second" cps)
+           cps)))
 
 (defvar-local subed--cps-overlay nil)
 
@@ -1633,19 +1639,16 @@ attribute(s)."
                 (end (save-excursion
                              (goto-char begin)
                              (line-end-position))))
-      (if (overlayp subed--cps-overlay)
+      (if subed--cps-overlay
                (move-overlay subed--cps-overlay begin end (current-buffer))
         (setq subed--cps-overlay (make-overlay begin end)))
       (subed--update-cps-overlay))))
 
 (defun subed--update-cps-overlay (&rest _rest)
-  "Update the CPS overlay.
-This accepts and ignores any number of arguments so that it can
-be run in `after-change-functions'."
-  (when (and (not subed--batch-editing)
-             (overlayp subed--cps-overlay))
+  "Update the CPS overlay."
+  (when subed--cps-overlay
     (let ((cps (subed-calculate-cps)))
-      (when (numberp cps)
+      (when cps
         (overlay-put
          subed--cps-overlay
          'after-string
diff --git a/subed/subed-srt.el b/subed/subed-srt.el
index 3a8a6d4a50..d1aa8cfae3 100644
--- a/subed/subed-srt.el
+++ b/subed/subed-srt.el
@@ -52,23 +52,20 @@
   "Find HH:MM:SS,MS pattern in TIME-STRING and convert it to milliseconds.
 Return nil if TIME-STRING doesn't match the pattern.
 Use the format-specific function for MAJOR-MODE."
-  (save-match-data
-    (when (string-match subed--regexp-timestamp time-string)
-      (let ((hours (string-to-number (match-string 1 time-string)))
-            (mins  (string-to-number (match-string 2 time-string)))
-            (secs  (string-to-number (match-string 3 time-string)))
-            (msecs (string-to-number (subed--right-pad (match-string 4 
time-string) 3 ?0))))
-        (+ (* (truncate hours) 3600000)
-           (* (truncate mins) 60000)
-           (* (truncate secs) 1000)
-           (truncate msecs))))))
+  (when (string-match subed--regexp-timestamp time-string)
+    (let ((hours (string-to-number (match-string 1 time-string)))
+          (mins  (string-to-number (match-string 2 time-string)))
+          (secs  (string-to-number (match-string 3 time-string)))
+          (msecs (string-to-number (subed--right-pad (match-string 4 
time-string) 3 ?0))))
+      (+ (* (truncate hours) 3600000)
+         (* (truncate mins) 60000)
+         (* (truncate secs) 1000)
+         (truncate msecs)))))
 
 (cl-defmethod subed--msecs-to-timestamp (msecs &context (major-mode 
subed-srt-mode))
   "Convert MSECS to string in the format HH:MM:SS,MS.
 Use the format-specific function for MAJOR-MODE."
-  ;; We need to wrap format-seconds in save-match-data because it does regexp
-  ;; stuff and we need to preserve our own match-data.
-  (concat (save-match-data (format-seconds "%02h:%02m:%02s" (/ msecs 1000)))
+  (concat (format-seconds "%02h:%02m:%02s" (/ msecs 1000))
           "," (format "%03d" (mod msecs 1000))))
 
 (cl-defmethod subed--subtitle-id (&context (major-mode subed-srt-mode))
@@ -82,26 +79,25 @@ Use the format-specific function for MAJOR-MODE."
   "Return the ID of the subtitle at MSECS milliseconds.
 Return nil if there is no subtitle at MSECS.
 Use the format-specific function for MAJOR-MODE."
-  (save-match-data
-    (save-excursion
-      (goto-char (point-min))
-      (let* ((secs       (/ msecs 1000))
-             (only-hours (truncate (/ secs 3600)))
-             (only-mins  (truncate (/ (- secs (* only-hours 3600)) 60))))
-        ;; Move to first subtitle in the relevant hour
-        (when (re-search-forward (format "\\(%s\\|\\`\\)[0-9]+\n%02d:" 
subed--regexp-separator only-hours) nil t)
-          (beginning-of-line)
-          ;; Move to first subtitle in the relevant hour and minute
-          (re-search-forward (format "\\(\n\n\\|\\`\\)[0-9]+\n%02d:%02d" 
only-hours only-mins) nil t)))
-      ;; Move to first subtitle that starts at or after MSECS
-      (catch 'subtitle-id
-        (while (<= (or (subed-subtitle-msecs-start) -1) msecs)
-          ;; If stop time is >= MSECS, we found a match
-          (let ((cur-sub-end (subed-subtitle-msecs-stop)))
-            (when (and cur-sub-end (>= cur-sub-end msecs))
-              (throw 'subtitle-id (subed-subtitle-id))))
-          (unless (subed-forward-subtitle-id)
-            (throw 'subtitle-id nil)))))))
+  (save-excursion
+    (goto-char (point-min))
+    (let* ((secs       (/ msecs 1000))
+           (only-hours (truncate (/ secs 3600)))
+           (only-mins  (truncate (/ (- secs (* only-hours 3600)) 60))))
+      ;; Move to first subtitle in the relevant hour
+      (when (re-search-forward (format "\\(%s\\|\\`\\)[0-9]+\n%02d:" 
subed--regexp-separator only-hours) nil t)
+        (beginning-of-line)
+        ;; Move to first subtitle in the relevant hour and minute
+        (re-search-forward (format "\\(\n\n\\|\\`\\)[0-9]+\n%02d:%02d" 
only-hours only-mins) nil t)))
+    ;; Move to first subtitle that starts at or after MSECS
+    (catch 'subtitle-id
+      (while (<= (or (subed-subtitle-msecs-start) -1) msecs)
+        ;; If stop time is >= MSECS, we found a match
+        (let ((cur-sub-end (subed-subtitle-msecs-stop)))
+          (when (and cur-sub-end (>= cur-sub-end msecs))
+            (throw 'subtitle-id (subed-subtitle-id))))
+        (unless (subed-forward-subtitle-id)
+          (throw 'subtitle-id nil))))))
 
 ;;; Traversing
 
@@ -110,51 +106,48 @@ Use the format-specific function for MAJOR-MODE."
 If SUB-ID is not given, focus the current subtitle's ID.
 Return point or nil if no subtitle ID could be found.
 Use the format-specific function for MAJOR-MODE."
-  (save-match-data
-    (if sub-id
-        ;; Look for a line that contains only the ID, preceded by one or more
-        ;; blank lines or the beginning of the buffer.
-        (let* ((orig-point (point))
-               (regex (format "\\(%s\\|\\`\\)\\(%d\\)$" 
subed--regexp-separator sub-id))
-               (match-found (progn (goto-char (point-min))
-                                   (re-search-forward regex nil t))))
-          (if match-found
-              (goto-char (match-beginning 2))
-            (goto-char orig-point)))
-      ;; Find one or more blank lines.
-      (re-search-forward "\\([[:blank:]]*\n\\)+" nil t)
-      ;; Find two or more blank lines or the beginning of the buffer, followed
-      ;; by line composed of only digits.
-      (let* ((regex (concat "\\(" subed--regexp-separator 
"\\|\\`\\)\\([0-9]+\\)$"))
-             (match-found (re-search-backward regex nil t)))
-        (when match-found
-          (goto-char (match-beginning 2)))))
-    ;; Make extra sure we're on an ID, return nil if we're not
-    (when (looking-at "^\\([0-9]+\\)$")
-      (point))))
+  (if sub-id
+      ;; Look for a line that contains only the ID, preceded by one or more
+      ;; blank lines or the beginning of the buffer.
+      (let* ((orig-point (point))
+             (regex (format "\\(%s\\|\\`\\)\\(%d\\)$" subed--regexp-separator 
sub-id))
+             (match-found (progn (goto-char (point-min))
+                                 (re-search-forward regex nil t))))
+        (if match-found
+            (goto-char (match-beginning 2))
+          (goto-char orig-point)))
+    ;; Find one or more blank lines.
+    (re-search-forward "\\([[:blank:]]*\n\\)+" nil t)
+    ;; Find two or more blank lines or the beginning of the buffer, followed
+    ;; by line composed of only digits.
+    (let* ((regex (concat "\\(" subed--regexp-separator 
"\\|\\`\\)\\([0-9]+\\)$"))
+           (match-found (re-search-backward regex nil t)))
+      (when match-found
+        (goto-char (match-beginning 2)))))
+  ;; Make extra sure we're on an ID, return nil if we're not
+  (when (looking-at "^\\([0-9]+\\)$")
+    (point)))
 
 (cl-defmethod subed--jump-to-subtitle-time-start (&context (major-mode 
subed-srt-mode) &optional sub-id)
   "Move point to subtitle's start time.
 If SUB-ID is not given, use subtitle on point.
 Return point or nil if no start time could be found.
 Use the format-specific function for MAJOR-MODE."
-  (save-match-data
-    (when (subed-jump-to-subtitle-id sub-id)
-      (forward-line)
-      (when (looking-at subed--regexp-timestamp)
-        (point)))))
+  (when (subed-jump-to-subtitle-id sub-id)
+    (forward-line)
+    (when (looking-at subed--regexp-timestamp)
+      (point))))
 
 (cl-defmethod subed--jump-to-subtitle-time-stop (&context (major-mode 
subed-srt-mode) &optional sub-id)
   "Move point to subtitle's stop time.
 If SUB-ID is not given, use subtitle on point.
 Return point or nil if no stop time could be found.
 Use the format-specific function for MAJOR-MODE."
-  (save-match-data
-    (when (subed-jump-to-subtitle-id sub-id)
-      (forward-line 1)
-      (re-search-forward " *--> *" (point-at-eol) t)
-      (when (looking-at subed--regexp-timestamp)
-        (point)))))
+  (when (subed-jump-to-subtitle-id sub-id)
+    (forward-line 1)
+    (re-search-forward " *--> *" (point-at-eol) t)
+    (when (looking-at subed--regexp-timestamp)
+      (point))))
 
 (cl-defmethod subed--jump-to-subtitle-text (&context (major-mode 
subed-srt-mode) &optional sub-id)
   "Move point on the first character of subtitle's text.
@@ -170,26 +163,24 @@ Use the format-specific function for MAJOR-MODE."
 If SUB-ID is not given, use subtitle on point.
 Return point or nil if point did not change or if no subtitle end
 can be found.  Use the format-specific function for MAJOR-MODE."
-  (save-match-data
-    (let ((orig-point (point)))
-      (subed-jump-to-subtitle-text sub-id)
-      ;; Look for next separator or end of buffer.  We can't use
-      ;; `subed-srt--regexp-separator' here because if subtitle text is empty,
-      ;; it may be the only empty line in the separator, i.e. there's only one
-      ;; "\n".
-      (let ((regex (concat 
"\\([[:blank:]]*\n+[0-9]+\n\\|\\([[:blank:]]*\n*\\)\\'\\)")))
-        (when (re-search-forward regex nil t)
-          (goto-char (match-beginning 0))))
-      (unless (= (point) orig-point)
-        (point)))))
+  (let ((orig-point (point)))
+    (subed-jump-to-subtitle-text sub-id)
+    ;; Look for next separator or end of buffer.  We can't use
+    ;; `subed-srt--regexp-separator' here because if subtitle text is empty,
+    ;; it may be the only empty line in the separator, i.e. there's only one
+    ;; "\n".
+    (let ((regex (concat 
"\\([[:blank:]]*\n+[0-9]+\n\\|\\([[:blank:]]*\n*\\)\\'\\)")))
+      (when (re-search-forward regex nil t)
+        (goto-char (match-beginning 0))))
+    (unless (= (point) orig-point)
+      (point))))
 
 (cl-defmethod subed--forward-subtitle-id (&context (major-mode subed-srt-mode))
   "Move point to next subtitle's ID.
 Return point or nil if there is no next subtitle.
 Use the format-specific function for MAJOR-MODE."
-  (save-match-data
-    (when (re-search-forward (concat subed--regexp-separator "[0-9]+\n") nil t)
-      (subed-jump-to-subtitle-id))))
+  (when (re-search-forward (concat subed--regexp-separator "[0-9]+\n") nil t)
+    (subed-jump-to-subtitle-id)))
 
 (cl-defmethod subed--backward-subtitle-id (&context (major-mode 
subed-srt-mode))
   "Move point to previous subtitle's ID.
@@ -234,9 +225,8 @@ Move point to the text of the inserted subtitle.
 Return new point.  Use the format-specific function for MAJOR-MODE."
   (subed-jump-to-subtitle-id)
   (insert (subed-make-subtitle id start stop text))
-  (save-match-data
-    (when (looking-at "\\([[:space:]]*\\|^\\)[0-9]+$")
-      (insert "\n")))
+  (when (looking-at "\\([[:space:]]*\\|^\\)[0-9]+$")
+    (insert "\n"))
   (forward-line -2)
   (subed-jump-to-subtitle-text))
 
@@ -259,9 +249,8 @@ Return new point.  Use the format-specific function for 
MAJOR-MODE."
     (goto-char (match-end 0)))
   (insert (subed-make-subtitle id start stop text))
   ;; Complete separator with another newline unless we inserted at the end
-  (save-match-data
-    (when (looking-at "\\([[:space:]]*\\|^\\)[0-9]+$")
-      (insert ?\n)))
+  (when (looking-at "\\([[:space:]]*\\|^\\)[0-9]+$")
+    (insert ?\n))
   (forward-line -2)
   (subed-jump-to-subtitle-text))
 
@@ -298,97 +287,94 @@ Use the format-specific function for MAJOR-MODE."
 (cl-defmethod subed--regenerate-ids (&context (major-mode subed-srt-mode))
   "Ensure consecutive, unduplicated subtitle IDs."
   (atomic-change-group
-    (save-match-data
-      (save-excursion
-        (goto-char (point-min))
-        (subed-jump-to-subtitle-id)
-        (when (looking-at "^[[:digit:]]+$")
-          (unless (string= (current-word) "1")
-            (delete-region (point) (progn (forward-word 1) (point)))
-            (insert "1")))
-        (let ((id 2))
-          (while (subed-forward-subtitle-id)
-            (let ((id-str (number-to-string id)))
-              (unless (string= (current-word) id-str)
-                (delete-region (point) (progn (forward-word 1) (point)))
-                (insert id-str)))
-            (setq id (1+ id))))))))
+    (save-excursion
+      (goto-char (point-min))
+      (subed-jump-to-subtitle-id)
+      (when (looking-at "^[[:digit:]]+$")
+        (unless (string= (current-word) "1")
+          (delete-region (point) (progn (forward-word 1) (point)))
+          (insert "1")))
+      (let ((id 2))
+        (while (subed-forward-subtitle-id)
+          (let ((id-str (number-to-string id)))
+            (unless (string= (current-word) id-str)
+              (delete-region (point) (progn (forward-word 1) (point)))
+              (insert id-str)))
+          (setq id (1+ id)))))))
 
 (cl-defmethod subed--sanitize-format (&context (major-mode subed-srt-mode))
   "Remove surplus newlines and whitespace.
 Use the format-specific function for MAJOR-MODE."
   (atomic-change-group
-    (save-match-data
-      (subed-save-excursion
-       ;; Remove trailing whitespace from each line
-       (delete-trailing-whitespace (point-min) (point-max))
-
-       ;; Remove leading spaces and tabs from each line
-       (goto-char (point-min))
-       (while (re-search-forward "^[[:blank:]]+" nil t)
-         (replace-match ""))
-
-       ;; Remove leading newlines
-       (goto-char (point-min))
-       (while (looking-at "\\`\n+")
-         (replace-match ""))
-
-       ;; Replace separators between subtitles with double newlines
-       (goto-char (point-min))
-       (while (subed-forward-subtitle-id)
-         (let ((prev-sub-end (save-excursion (when 
(subed-backward-subtitle-end)
-                                               (point)))))
-           (when (and prev-sub-end
-                      (not (string= (buffer-substring prev-sub-end (point)) 
"\n\n")))
-             (delete-region prev-sub-end (point))
-             (insert "\n\n"))))
-
-       ;; Two trailing newline if last subtitle text is empty, one trailing
-       ;; newline otherwise; do nothing in empty buffer (no graphical
-       ;; characters)
-       (goto-char (point-min))
-       (when (re-search-forward "[[:graph:]]" nil t)
-         (goto-char (point-max))
-         (subed-jump-to-subtitle-end)
-         (unless (looking-at "\n\\'")
-           (delete-region (point) (point-max))
-           (insert "\n")))
-
-       ;; One space before and after " --> "
-       (goto-char (point-min))
-       (while (re-search-forward (format "^%s" subed--regexp-timestamp) nil t)
-         (when (looking-at "[[:blank:]]*-->[[:blank:]]*")
-           (unless (= (length (match-string 0)) 5)
-             (replace-match " --> "))))))))
+    (subed-save-excursion
+     ;; Remove trailing whitespace from each line
+     (delete-trailing-whitespace (point-min) (point-max))
+
+     ;; Remove leading spaces and tabs from each line
+     (goto-char (point-min))
+     (while (re-search-forward "^[[:blank:]]+" nil t)
+       (replace-match ""))
+
+     ;; Remove leading newlines
+     (goto-char (point-min))
+     (while (looking-at "\\`\n+")
+       (replace-match ""))
+
+     ;; Replace separators between subtitles with double newlines
+     (goto-char (point-min))
+     (while (subed-forward-subtitle-id)
+       (let ((prev-sub-end (save-excursion (when (subed-backward-subtitle-end)
+                                             (point)))))
+         (when (and prev-sub-end
+                    (not (string= (buffer-substring prev-sub-end (point)) 
"\n\n")))
+           (delete-region prev-sub-end (point))
+           (insert "\n\n"))))
+
+     ;; Two trailing newline if last subtitle text is empty, one trailing
+     ;; newline otherwise; do nothing in empty buffer (no graphical
+     ;; characters)
+     (goto-char (point-min))
+     (when (re-search-forward "[[:graph:]]" nil t)
+       (goto-char (point-max))
+       (subed-jump-to-subtitle-end)
+       (unless (looking-at "\n\\'")
+         (delete-region (point) (point-max))
+         (insert "\n")))
+
+     ;; One space before and after " --> "
+     (goto-char (point-min))
+     (while (re-search-forward (format "^%s" subed--regexp-timestamp) nil t)
+       (when (looking-at "[[:blank:]]*-->[[:blank:]]*")
+         (unless (= (length (match-string 0)) 5)
+           (replace-match " --> ")))))))
 
 (cl-defmethod subed--validate-format (&context (major-mode subed-srt-mode))
   "Move point to the first invalid subtitle and report an error.
 Use the format-specific function for MAJOR-MODE."
   (when (> (buffer-size) 0)
     (atomic-change-group
-      (save-match-data
-        (let ((orig-point (point)))
-          (goto-char (point-min))
-          (while (and (re-search-forward (format "\\(%s\\|\\`\\)" 
subed--regexp-separator)
-                                         nil t)
-                      (looking-at "[[:alnum:]]"))
-            (unless (looking-at "^[0-9]+$")
-              (error "Found invalid subtitle ID: %S" (substring (or 
(thing-at-point 'line :no-properties) "\n") 0 -1)))
-            (forward-line)
-            ;; This regex is stricter than `subed-srt--regexp-timestamp'
-            (unless (looking-at 
"^[0-9]\\{2\\}:[0-9]\\{2\\}:[0-9]\\{2\\},[0-9]\\{,3\\}")
-              (error "Found invalid start time: %S"  (substring (or 
(thing-at-point 'line :no-properties) "\n") 0 -1)))
-            (when (re-search-forward "[[:blank:]]" (point-at-eol) t)
-              (goto-char (match-beginning 0)))
-            (unless (looking-at " --> ")
-              (error "Found invalid separator between start and stop time: %S"
-                     (substring (or (thing-at-point 'line :no-properties) 
"\n") 0 -1)))
-            (condition-case nil
-                (forward-char 5)
-              (error nil))
-            (unless (looking-at 
"[0-9]\\{2\\}:[0-9]\\{2\\}:[0-9]\\{2\\},[0-9]\\{,3\\}$")
-              (error "Found invalid stop time: %S" (substring (or 
(thing-at-point 'line :no-properties) "\n") 0 -1))))
-          (goto-char orig-point))))))
+      (let ((orig-point (point)))
+        (goto-char (point-min))
+        (while (and (re-search-forward (format "\\(%s\\|\\`\\)" 
subed--regexp-separator)
+                                       nil t)
+                    (looking-at "[[:alnum:]]"))
+          (unless (looking-at "^[0-9]+$")
+            (error "Found invalid subtitle ID: %S" (substring (or 
(thing-at-point 'line :no-properties) "\n") 0 -1)))
+          (forward-line)
+          ;; This regex is stricter than `subed-srt--regexp-timestamp'
+          (unless (looking-at 
"^[0-9]\\{2\\}:[0-9]\\{2\\}:[0-9]\\{2\\},[0-9]\\{,3\\}")
+            (error "Found invalid start time: %S"  (substring (or 
(thing-at-point 'line :no-properties) "\n") 0 -1)))
+          (when (re-search-forward "[[:blank:]]" (point-at-eol) t)
+            (goto-char (match-beginning 0)))
+          (unless (looking-at " --> ")
+            (error "Found invalid separator between start and stop time: %S"
+                   (substring (or (thing-at-point 'line :no-properties) "\n") 
0 -1)))
+          (condition-case nil
+              (forward-char 5)
+            (error nil))
+          (unless (looking-at 
"[0-9]\\{2\\}:[0-9]\\{2\\}:[0-9]\\{2\\},[0-9]\\{,3\\}$")
+            (error "Found invalid stop time: %S" (substring (or 
(thing-at-point 'line :no-properties) "\n") 0 -1))))
+        (goto-char orig-point)))))
 
 (cl-defmethod subed--insert-subtitle :after (&context (major-mode 
subed-srt-mode) &optional arg)
   "Renumber afterwards. Format-specific for MAJOR-MODE."
diff --git a/subed/subed-vtt.el b/subed/subed-vtt.el
index 908ab8fa03..8554d743ff 100644
--- a/subed/subed-vtt.el
+++ b/subed/subed-vtt.el
@@ -46,28 +46,30 @@
 
 (defconst subed-vtt--regexp-timestamp 
"\\(\\([0-9]+\\):\\)?\\([0-9]+\\):\\([0-9]+\\)\\.\\([0-9]+\\)")
 (defconst subed-vtt--regexp-separator "\\(?:[[:blank:]]*\n\\)+\\(?:NOTE[ 
\n]\\(?:.+?\n\\)+\n\\)*\n")
+(defconst subed-vtt--regexp-identifier
+  ;; According to https://developer.mozilla.org/en-US/docs/Web/API/WebVTT_API
+  ;; Cues can start with an identifier which is a non empty line that does
+  ;; not contain "-->".
+  "^[ \t]*[^ \t\n-]\\(?:[^\n-]\\|-[^\n-]\\|--[^\n>]\\)*[ \t]*\n")
 
 (cl-defmethod subed--timestamp-to-msecs (time-string &context (major-mode 
subed-vtt-mode))
   "Find HH:MM:SS.MS pattern in TIME-STRING and convert it to milliseconds.
 Return nil if TIME-STRING doesn't match the pattern.
 Use the format-specific function for MAJOR-MODE."
-  (save-match-data
-    (when (string-match subed--regexp-timestamp time-string)
-      (let ((hours (string-to-number (or (match-string 2 time-string) "0")))
-            (mins  (string-to-number (match-string 3 time-string)))
-            (secs  (string-to-number (match-string 4 time-string)))
-            (msecs (string-to-number (subed--right-pad (match-string 5 
time-string) 3 ?0))))
-        (+ (* (truncate hours) 3600000)
-           (* (truncate mins) 60000)
-           (* (truncate secs) 1000)
-           (truncate msecs))))))
+  (when (string-match subed--regexp-timestamp time-string)
+    (let ((hours (string-to-number (or (match-string 2 time-string) "0")))
+          (mins  (string-to-number (match-string 3 time-string)))
+          (secs  (string-to-number (match-string 4 time-string)))
+          (msecs (string-to-number (subed--right-pad (match-string 5 
time-string) 3 ?0))))
+      (+ (* (truncate hours) 3600000)
+         (* (truncate mins) 60000)
+         (* (truncate secs) 1000)
+         (truncate msecs)))))
 
 (cl-defmethod subed--msecs-to-timestamp (msecs &context (major-mode 
subed-vtt-mode))
   "Convert MSECS to string in the format HH:MM:SS.MS.
 Use the format-specific function for MAJOR-MODE."
-  ;; We need to wrap format-seconds in save-match-data because it does regexp
-  ;; stuff and we need to preserve our own match-data.
-  (concat (save-match-data (format-seconds "%02h:%02m:%02s" (/ msecs 1000)))
+  (concat (format-seconds "%02h:%02m:%02s" (/ msecs 1000))
           "." (format "%03d" (mod msecs 1000))))
 
 (cl-defmethod subed--subtitle-id (&context (major-mode subed-vtt-mode))
@@ -82,26 +84,25 @@ Use the format-specific function for MAJOR-MODE."
   "Return the ID of the subtitle at MSECS milliseconds.
 Return nil if there is no subtitle at MSECS.  Use the
 format-specific function for MAJOR-MODE."
-  (save-match-data
-    (save-excursion
-      (goto-char (point-min))
-      (let* ((secs       (/ msecs 1000))
-             (only-hours (truncate (/ secs 3600)))
-             (only-mins  (truncate (/ (- secs (* only-hours 3600)) 60))))
-        ;; Move to first subtitle in the relevant hour
-        (when (re-search-forward (format "\\(%s\\|\\`\\)%02d:" 
subed--regexp-separator only-hours) nil t)
-          (beginning-of-line)
-          ;; Move to first subtitle in the relevant hour and minute
-          (re-search-forward (format "\\(\n\n\\|\\`\\)%02d:%02d" only-hours 
only-mins) nil t)))
-      ;; Move to first subtitle that starts at or after MSECS
-      (catch 'subtitle-id
-        (while (<= (or (subed-subtitle-msecs-start) -1) msecs)
-          ;; If stop time is >= MSECS, we found a match
-          (let ((cur-sub-end (subed-subtitle-msecs-stop)))
-            (when (and cur-sub-end (>= cur-sub-end msecs))
-              (throw 'subtitle-id (subed-subtitle-id))))
-          (unless (subed-forward-subtitle-id)
-            (throw 'subtitle-id nil)))))))
+  (save-excursion
+    (goto-char (point-min))
+    (let* ((secs       (/ msecs 1000))
+           (only-hours (truncate (/ secs 3600)))
+           (only-mins  (truncate (/ (- secs (* only-hours 3600)) 60))))
+      ;; Move to first subtitle in the relevant hour
+      (when (re-search-forward (format "\\(%s\\|\\`\\)%02d:" 
subed--regexp-separator only-hours) nil t)
+        (beginning-of-line)
+        ;; Move to first subtitle in the relevant hour and minute
+        (re-search-forward (format "\\(\n\n\\|\\`\\)%02d:%02d" only-hours 
only-mins) nil t)))
+    ;; Move to first subtitle that starts at or after MSECS
+    (catch 'subtitle-id
+      (while (<= (or (subed-subtitle-msecs-start) -1) msecs)
+        ;; If stop time is >= MSECS, we found a match
+        (let ((cur-sub-end (subed-subtitle-msecs-stop)))
+          (when (and cur-sub-end (>= cur-sub-end msecs))
+            (throw 'subtitle-id (subed-subtitle-id))))
+        (unless (subed-forward-subtitle-id)
+          (throw 'subtitle-id nil))))))
 
 ;;; Traversing
 
@@ -111,49 +112,48 @@ If SUB-ID is not given, focus the current subtitle's ID.
 Return point or nil if no subtitle ID could be found.
 WebVTT doesn't use IDs, so we use the starting timestamp instead.
 Use the format-specific function for MAJOR-MODE."
-  (save-match-data
-    (if (stringp sub-id)
-        ;; Look for a line that contains the timestamp, preceded by one or more
-        ;; blank lines or the beginning of the buffer.
-        (let* ((orig-point (point))
-               (regex (concat "\\(" subed--regexp-separator "\\|\\`\\)\\(" 
(regexp-quote sub-id) "\\)"))
-               (match-found (progn (goto-char (point-min))
-                                   (re-search-forward regex nil t))))
-          (if match-found
-              (goto-char (match-beginning 2))
-            (goto-char orig-point)))
-      ;; Find one or more blank lines.
-      (re-search-forward "\\([[:blank:]]*\n\\)+" nil t)
-      ;; Find two or more blank lines or the beginning of the buffer, followed
-      ;; by line starting with a timestamp.
-      (let* ((regex (concat  "\\(" subed--regexp-separator "\\|\\`\\)\\(" 
subed--regexp-timestamp "\\)"))
-             (match-found (re-search-backward regex nil t)))
-        (when match-found
-          (goto-char (match-beginning 2)))))
-    ;; Make extra sure we're on a timestamp, return nil if we're not
-    (when (looking-at "^\\(\\([0-9]+:\\)?[0-9]+:[0-9]+\\.[0-9]+\\)")
-      (point))))
+  (if (stringp sub-id)
+      ;; Look for a line that contains the timestamp, preceded by one or more
+      ;; blank lines or the beginning of the buffer.
+      (let* ((orig-point (point))
+             (regex (concat "\\(" subed--regexp-separator "\\|\\`\\)\\(" 
(regexp-quote sub-id) "\\)"))
+             (match-found (progn (goto-char (point-min))
+                                 (re-search-forward regex nil t))))
+        (if match-found
+            (goto-char (match-beginning 2))
+          (goto-char orig-point)))
+    ;; Find one or more blank lines.
+    (re-search-forward "\\([[:blank:]]*\n\\)+" nil t)
+    ;; Find two or more blank lines or the beginning of the buffer, followed
+    ;; by line starting with a timestamp.
+    (let* ((regex (concat  "\\(" subed--regexp-separator "\\|\\`\\)"
+                           "\\(?:" subed-vtt--regexp-identifier "\\)?"
+                           "\\(" subed--regexp-timestamp "\\)"))
+           (match-found (re-search-backward regex nil t)))
+      (when match-found
+        (goto-char (match-beginning 2)))))
+  ;; Make extra sure we're on a timestamp, return nil if we're not
+  (when (looking-at "^\\(\\([0-9]+:\\)?[0-9]+:[0-9]+\\.[0-9]+\\)")
+    (point)))
 
 (cl-defmethod subed--jump-to-subtitle-time-start (&context (major-mode 
subed-vtt-mode) &optional sub-id)
   "Move point to subtitle's start time.
 If SUB-ID is not given, use subtitle on point.
 Return point or nil if no start time could be found.
 Use the format-specific function for MAJOR-MODE."
-  (save-match-data
-    (when (subed-jump-to-subtitle-id sub-id)
-      (when (looking-at subed--regexp-timestamp)
-        (point)))))
+  (when (subed-jump-to-subtitle-id sub-id)
+    (when (looking-at subed--regexp-timestamp)
+      (point))))
 
 (cl-defmethod subed--jump-to-subtitle-time-stop (&context (major-mode 
subed-vtt-mode) &optional sub-id)
   "Move point to subtitle's stop time.
 If SUB-ID is not given, use subtitle on point.
 Return point or nil if no stop time could be found.
 Use the format-specific function for MAJOR-MODE."
-  (save-match-data
-    (when (subed-jump-to-subtitle-id sub-id)
-      (re-search-forward " *--> *" (point-at-eol) t)
-      (when (looking-at subed--regexp-timestamp)
-        (point)))))
+  (when (subed-jump-to-subtitle-id sub-id)
+    (re-search-forward " *--> *" (point-at-eol) t)
+    (when (looking-at subed--regexp-timestamp)
+      (point))))
 
 (cl-defmethod subed--jump-to-subtitle-text (&context (major-mode 
subed-vtt-mode) &optional sub-id)
   "Move point on the first character of subtitle's text.
@@ -169,28 +169,29 @@ Use the format-specific function for MAJOR-MODE."
 If SUB-ID is not given, use subtitle on point.
 Return point or nil if point did not change or if no subtitle end
 can be found.  Use the format-specific function for MAJOR-MODE."
-  (save-match-data
-    (let ((orig-point (point)))
-      (subed-jump-to-subtitle-text sub-id)
-      ;; Look for next separator or end of buffer.  We can't use
-      ;; `subed-vtt--regexp-separator' here because if subtitle text is empty,
-      ;; it may be the only empty line in the separator, i.e. there's only one
-      ;; "\n".
-      (let ((regex (concat "\\([[:blank:]]*\n\\)+"
-                           "\\(?:NOTE[ \n]\\(?:.+?\n\\)+\n\\)*"
-                           "\\(" subed--regexp-timestamp 
"\\)\\|\\([[:blank:]]*\n*\\)\\'")))
-        (when (re-search-forward regex nil t)
-          (goto-char (match-beginning 0))))
-      (unless (= (point) orig-point)
-        (point)))))
+  (let ((orig-point (point)))
+    (subed-jump-to-subtitle-text sub-id)
+    ;; Look for next separator or end of buffer.  We can't use
+    ;; `subed-vtt--regexp-separator' here because if subtitle text is empty,
+    ;; it may be the only empty line in the separator, i.e. there's only one
+    ;; "\n".
+    (let ((regex (concat "\\([[:blank:]]*\n\\)+"
+                         "\\(?:NOTE[ \n]\\(?:.+?\n\\)+\n\\)*"
+                         "\\(" subed--regexp-timestamp 
"\\)\\|\\([[:blank:]]*\n*\\)\\'")))
+      (when (re-search-forward regex nil t)
+        (goto-char (match-beginning 0))))
+    (unless (= (point) orig-point)
+      (point))))
 
 (cl-defmethod subed--forward-subtitle-id (&context (major-mode subed-vtt-mode))
   "Move point to next subtitle's ID.
 Return point or nil if there is no next subtitle.  Use the
 format-specific function for MAJOR-MODE."
-  (save-match-data
-    (when (re-search-forward (concat subed--regexp-separator 
subed--regexp-timestamp) nil t)
-      (subed-jump-to-subtitle-id))))
+  (when (re-search-forward (concat subed--regexp-separator
+                                   "\\(?:" subed-vtt--regexp-identifier "\\)?"
+                                   subed--regexp-timestamp)
+                           nil t)
+    (subed-jump-to-subtitle-id)))
 
 (cl-defmethod subed--backward-subtitle-id (&context (major-mode 
subed-vtt-mode))
   "Move point to previous subtitle's ID.
@@ -234,9 +235,8 @@ Move point to the text of the inserted subtitle.  Return new
 point.  Use the format-specific function for MAJOR-MODE."
   (subed-jump-to-subtitle-id)
   (insert (subed-make-subtitle id start stop text))
-  (save-match-data
-    (when (looking-at (concat "\\([[:space:]]*\\|^\\)" 
subed--regexp-timestamp))
-      (insert "\n")))
+  (when (looking-at (concat "\\([[:space:]]*\\|^\\)" subed--regexp-timestamp))
+    (insert "\n"))
   (forward-line -2)
   (subed-jump-to-subtitle-text))
 
@@ -259,9 +259,8 @@ point.  Use the format-specific function for MAJOR-MODE."
     (goto-char (match-end 0)))
   (insert (subed-make-subtitle id start stop text))
   ;; Complete separator with another newline unless we inserted at the end
-  (save-match-data
-    (when (looking-at (concat "\\([[:space:]]*\\|^\\)" 
subed--regexp-timestamp))
-      (insert ?\n)))
+  (when (looking-at (concat "\\([[:space:]]*\\|^\\)" subed--regexp-timestamp))
+    (insert ?\n))
   (forward-line -2)
   (subed-jump-to-subtitle-text))
 
@@ -289,73 +288,71 @@ Use the format-specific function for MAJOR-MODE."
   "Remove surplus newlines and whitespace.
 Use the format-specific function for MAJOR-MODE."
   (atomic-change-group
-    (save-match-data
-      (subed-save-excursion
-       ;; Remove trailing whitespace from each line
-       (delete-trailing-whitespace (point-min) (point-max))
-
-       ;; Remove leading spaces and tabs from each line
-       (goto-char (point-min))
-       (while (re-search-forward "^[[:blank:]]+" nil t)
-         (replace-match ""))
-
-       ;; Remove leading newlines
-       (goto-char (point-min))
-       (while (looking-at "\\`\n+")
-         (replace-match ""))
-
-       ;; Replace separators between subtitles with double newlines
-       (goto-char (point-min))
-       (while (subed-forward-subtitle-id)
-         (let ((prev-sub-end (save-excursion (when 
(subed-backward-subtitle-end)
-                                               (point)))))
-           (when (and prev-sub-end
-                      (not (string= (buffer-substring prev-sub-end (point)) 
"\n\n")))
-             (delete-region prev-sub-end (point))
-             (insert "\n\n"))))
-
-       ;; Two trailing newline if last subtitle text is empty, one trailing
-       ;; newline otherwise; do nothing in empty buffer (no graphical
-       ;; characters)
-       (goto-char (point-min))
-       (when (re-search-forward "[[:graph:]]" nil t)
-         (goto-char (point-max))
-         (subed-jump-to-subtitle-end)
-         (unless (looking-at "\n\\'")
-           (delete-region (point) (point-max))
-           (insert "\n")))
-
-       ;; One space before and after " --> "
-       (goto-char (point-min))
-       (while (re-search-forward (format "^%s" subed--regexp-timestamp) nil t)
-         (when (looking-at "[[:blank:]]*-->[[:blank:]]*")
-           (unless (= (length (match-string 0)) 5)
-             (replace-match " --> "))))))))
+    (subed-save-excursion
+     ;; Remove trailing whitespace from each line
+     (delete-trailing-whitespace (point-min) (point-max))
+
+     ;; Remove leading spaces and tabs from each line
+     (goto-char (point-min))
+     (while (re-search-forward "^[[:blank:]]+" nil t)
+       (replace-match ""))
+
+     ;; Remove leading newlines
+     (goto-char (point-min))
+     (while (looking-at "\\`\n+")
+       (replace-match ""))
+
+     ;; Replace separators between subtitles with double newlines
+     (goto-char (point-min))
+     (while (subed-forward-subtitle-id)
+       (let ((prev-sub-end (save-excursion (when (subed-backward-subtitle-end)
+                                             (point)))))
+         (when (and prev-sub-end
+                    (not (string= (buffer-substring prev-sub-end (point)) 
"\n\n")))
+           (delete-region prev-sub-end (point))
+           (insert "\n\n"))))
+
+     ;; Two trailing newline if last subtitle text is empty, one trailing
+     ;; newline otherwise; do nothing in empty buffer (no graphical
+     ;; characters)
+     (goto-char (point-min))
+     (when (re-search-forward "[[:graph:]]" nil t)
+       (goto-char (point-max))
+       (subed-jump-to-subtitle-end)
+       (unless (looking-at "\n\\'")
+         (delete-region (point) (point-max))
+         (insert "\n")))
+
+     ;; One space before and after " --> "
+     (goto-char (point-min))
+     (while (re-search-forward (format "^%s" subed--regexp-timestamp) nil t)
+       (when (looking-at "[[:blank:]]*-->[[:blank:]]*")
+         (unless (= (length (match-string 0)) 5)
+           (replace-match " --> ")))))))
 
 (cl-defmethod subed--validate-format (&context (major-mode subed-vtt-mode))
   "Move point to the first invalid subtitle and report an error.
 Use the format-specific function for MAJOR-MODE."
   (when (> (buffer-size) 0)
     (atomic-change-group
-      (save-match-data
-        (let ((orig-point (point)))
-          (goto-char (point-min))
-          (while (and (re-search-forward (format "^\\(%s\\)" 
subed--regexp-timestamp) nil t)
-                      (goto-char (match-beginning 1)))
-            ;; This regex is stricter than `subed--regexp-timestamp'
-            (unless (looking-at 
"^[0-9]\\{2\\}:[0-9]\\{2\\}:[0-9]\\{2\\}\\(\\.[0-9]\\{0,3\\}\\)")
-              (error "Found invalid start time: %S"  (substring (or 
(thing-at-point 'line :no-properties) "\n") 0 -1)))
-            (when (re-search-forward "[[:blank:]]" (point-at-eol) t)
-              (goto-char (match-beginning 0)))
-            (unless (looking-at " --> ")
-              (error "Found invalid separator between start and stop time: %S"
-                     (substring (or (thing-at-point 'line :no-properties) 
"\n") 0 -1)))
-            (condition-case nil
-                (forward-char 5)
-              (error nil))
-            (unless (looking-at 
"[0-9]\\{2\\}:[0-9]\\{2\\}:[0-9]\\{2\\}\\(\\.[0-9]\\{0,3\\}\\)$")
-              (error "Found invalid stop time: %S" (substring (or 
(thing-at-point 'line :no-properties) "\n") 0 -1))))
-          (goto-char orig-point))))))
+      (let ((orig-point (point)))
+        (goto-char (point-min))
+        (while (and (re-search-forward (format "^\\(%s\\)" 
subed--regexp-timestamp) nil t)
+                    (goto-char (match-beginning 1)))
+          ;; This regex is stricter than `subed--regexp-timestamp'
+          (unless (looking-at 
"^[0-9]\\{2\\}:[0-9]\\{2\\}:[0-9]\\{2\\}\\(\\.[0-9]\\{0,3\\}\\)")
+            (error "Found invalid start time: %S"  (substring (or 
(thing-at-point 'line :no-properties) "\n") 0 -1)))
+          (when (re-search-forward "[[:blank:]]" (point-at-eol) t)
+            (goto-char (match-beginning 0)))
+          (unless (looking-at " --> ")
+            (error "Found invalid separator between start and stop time: %S"
+                   (substring (or (thing-at-point 'line :no-properties) "\n") 
0 -1)))
+          (condition-case nil
+              (forward-char 5)
+            (error nil))
+          (unless (looking-at 
"[0-9]\\{2\\}:[0-9]\\{2\\}:[0-9]\\{2\\}\\(\\.[0-9]\\{0,3\\}\\)$")
+            (error "Found invalid stop time: %S" (substring (or 
(thing-at-point 'line :no-properties) "\n") 0 -1))))
+        (goto-char orig-point)))))
 
 ;;;###autoload
 (define-derived-mode subed-vtt-mode subed-mode "Subed-VTT"
@@ -364,22 +361,29 @@ Use the format-specific function for MAJOR-MODE."
   (setq-local subed--regexp-timestamp subed-vtt--regexp-timestamp)
   (setq-local subed--regexp-separator subed-vtt--regexp-separator)
   (setq-local font-lock-defaults '(subed-vtt-font-lock-keywords))
+  (setq-local syntax-propertize-function
+              (syntax-propertize-rules
+               ("^\n" (0 ">"))
+               ("^\\(N\\)\\(O\\)TE\\_>" (1 "w 1") (2 "w 2"))))
   ;; Support for fill-paragraph (M-q)
   (let ((timestamps-regexp (concat subed--regexp-timestamp
                                    " *--> *"
                                    subed--regexp-timestamp)))
-    (setq-local paragraph-separate (concat "^\\("
-                                           (mapconcat 'identity 
`("[[:blank:]]*"
-                                                                  
"[[:digit:]]+"
-                                                                  
,timestamps-regexp) "\\|")
-                                           "\\)$"))
-    (setq-local paragraph-start (concat "\\("
-                                        ;; Mulitple speakers in the same
-                                        ;; subtitle are often distinguished 
with
-                                        ;; a "-" at the start of the line.
-                                        (mapconcat 'identity '("^-"
-                                                               
"[[:graph:]]*$") "\\|")
-                                        "\\)"))))
+    (setq-local paragraph-separate
+                (concat "^\\("
+                        (mapconcat #'identity `("[[:blank:]]*"
+                                                "[[:digit:]]+"
+                                                ,timestamps-regexp)
+                                   "\\|")
+                        "\\)$"))
+    (setq-local paragraph-start
+                (concat "\\("
+                        ;; Multiple speakers in the same
+                        ;; subtitle are often distinguished with
+                        ;; a "-" at the start of the line.
+                        (mapconcat #'identity '("^-" "[[:graph:]]*$")
+                                   "\\|")
+                        "\\)"))))
 
 ;;;###autoload
 (add-to-list 'auto-mode-alist '("\\.vtt\\'" . subed-vtt-mode))



reply via email to

[Prev in Thread] Current Thread [Next in Thread]