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

    subed-vtt: don't require blank lines before timestamps, just start of line
    
    * subed/subed-common.el (subed--post-command-handler):
    Save match data, just in case.
    
    * subed/subed-vtt.el (subed-vtt--regexp-timestamp):
    Don't capture any groups.
    (subed-vtt--regexp-blank-separator):
    Accept beginning of buffer.
    (subed-vtt--regexp-note):
    Remove extra character.
    (subed-vtt--regexp-identifier):
    Remove line separator.
    (subed-vtt--regexp-start-of-line):
    New regexp.
    (subed-vtt--regexp-timing):
    Ignore spaces before the timing.
    (subed--in-header-p, subed--in-comment-p,
    subed--jump-to-subtitle-id,
    subed--jump-to-subtitle-end,
    subed--forward-subtitle-id): Don't
    require blank lines before timestamps.
    * tests/test-subed-vtt.el ("subed-vtt"): New tests.
---
 subed/subed-common.el   |  49 ++++-----
 subed/subed-vtt.el      | 264 +++++++++++++++++++++++-------------------------
 tests/test-subed-vtt.el | 187 +++++++++++++++++++++++++++++++++-
 3 files changed, 336 insertions(+), 164 deletions(-)

diff --git a/subed/subed-common.el b/subed/subed-common.el
index 872fa3eaf8..9c55db0cbf 100644
--- a/subed/subed-common.el
+++ b/subed/subed-common.el
@@ -798,30 +798,31 @@ scheduled call is canceled and another call is scheduled 
in
   "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))
-        (new-buffer-chars-modified-tick (buffer-chars-modified-tick)))
-    (when (and
-           new-point subed--current-point
-           (not (and (= new-point subed--current-point)
-                     (= 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
-            subed--buffer-chars-modified-tick new-buffer-chars-modified-tick)
-      (run-hooks 'subed-point-motion-hook)
-
-      ;; Check if point moved across subtitle boundaries.
-      (let ((new-sub-id (subed-subtitle-id)))
-        (when (and new-sub-id subed--current-subtitle-id
-                   (not (funcall (if (stringp subed--current-subtitle-id) 
'string= 'equal)
-                                 new-sub-id subed--current-subtitle-id)))
-          ;; Store new ID and fire signal.
-          (setq subed--current-subtitle-id new-sub-id)
-          (run-hooks 'subed-subtitle-motion-hook))))))
+  (save-match-data
+    (let ((new-point (point))
+          (new-buffer-chars-modified-tick (buffer-chars-modified-tick)))
+      (when (and
+             new-point subed--current-point
+             (not (and (= new-point subed--current-point)
+                       (= 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
+              subed--buffer-chars-modified-tick new-buffer-chars-modified-tick)
+        (run-hooks 'subed-point-motion-hook)
+
+        ;; Check if point moved across subtitle boundaries.
+        (let ((new-sub-id (subed-subtitle-id)))
+          (when (and new-sub-id subed--current-subtitle-id
+                     (not (funcall (if (stringp subed--current-subtitle-id) 
'string= 'equal)
+                                   new-sub-id subed--current-subtitle-id)))
+            ;; Store new ID and fire signal.
+            (setq subed--current-subtitle-id new-sub-id)
+            (run-hooks 'subed-subtitle-motion-hook)))))))
 
 
 ;;; Adjusting start/stop time individually
diff --git a/subed/subed-vtt.el b/subed/subed-vtt.el
index a053e951fd..5c6e566e6d 100644
--- a/subed/subed-vtt.el
+++ b/subed/subed-vtt.el
@@ -43,18 +43,19 @@
 
 ;;; Parsing
 
-(defconst subed-vtt--regexp-timestamp 
"\\(?:\\(?:[0-9]+\\):\\)?\\(?:[0-9]+\\):\\(?:[0-9]+\\)\\(?:\\.\\([0-9]+\\)\\)?")
+(defconst subed-vtt--regexp-timestamp 
"\\(?:\\(?:[0-9]+\\):\\)?\\(?:[0-9]+\\):\\(?:[0-9]+\\)\\(?:\\.\\(?:[0-9]+\\)\\)?")
 (defconst subed-vtt--regexp-separator "\\(?:\\(?:[ \t]*\n\\)+\\(?:NOTE[ 
\t\n]*[ \t]*\n[ \t]*\n\\)?\\)"
   "Blank lines and possibly a comment.")
-(defconst subed-vtt--regexp-blank-separator "[ \t]*\n\\(?:[ \t]*\n\\)+")
-(defconst subed-vtt--regexp-note "\\(NOTE[ \s\t\n]\\)")
+(defconst subed-vtt--regexp-blank-separator "\\(?:[ \t]*\n[ \t]*\n\\|\\`\\(?:[ 
\t\n]*\\)\\)")
+(defconst subed-vtt--regexp-note "\\(NOTE[ \t\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")
+  "[ \t]*[^ \t\n-]\\(?:[^\n-]\\|-[^\n-]\\|--[^\n>]\\)*[ \t]*")
+(defconst subed-vtt--regexp-start-of-line "\\(?:\\`\\|\n\\)")
 (defconst subed-vtt--regexp-timing
-  (concat subed-vtt--regexp-timestamp "[ \t]*-->[ \t]*" 
subed-vtt--regexp-timestamp))
+  (concat "[ \t]*" subed-vtt--regexp-timestamp "[ \t]*-->[ \t]*" 
subed-vtt--regexp-timestamp))
 (defconst subed-vtt--regexp-maybe-identifier-and-timing
   (format "%s\\(%s%s\\|%s\\)"
           subed-vtt--regexp-blank-separator
@@ -115,60 +116,55 @@ format-specific function for MAJOR-MODE."
 ;;; Traversing
 
 (cl-defmethod subed--in-header-p (&context (major-mode subed-vtt-mode))
-  "Return non-nil if the point is in the file header."
+  "Return non-nil if the point is in the file header.
+Use the format-specific function for MAJOR-MODE."
   (save-excursion
     (let ((orig-point (point)))
-      (goto-char (point-min))
-      (if (looking-at (format "[ \t\n]*\\(%s%s\\|%s\\|%s\\)"
-                              subed-vtt--regexp-note
-                              subed-vtt--regexp-identifier
-                              subed-vtt--regexp-timing
-                              subed-vtt--regexp-timing))
-          (< orig-point (match-beginning 1))
-        (if (re-search-forward (format "%s\\(%s\\|%s%s\\|%s\\)"
-                                       subed-vtt--regexp-blank-separator
-                                       subed-vtt--regexp-note
-                                       subed-vtt--regexp-identifier
-                                       subed-vtt--regexp-timing
-                                       subed-vtt--regexp-timing)
-                               nil t)
-            (< orig-point (match-beginning 1))
-          t)))))
-
-(cl-defmethod subed--in-comment-p (&context (major-mode subed-vtt-mode))
-  "Return non-nil if the point is in a comment."
-  (let* ((orig-point (point))
-         (previous-comment
-          (save-excursion
-            (forward-line 1)
-            (when (re-search-backward
-                   (format "%s\\(%s\\)"
-                           subed-vtt--regexp-blank-separator
-                           subed-vtt--regexp-note)
-                   nil t)
-              (match-beginning 1))))
-         (previous-timestamp
-          (save-excursion
-            (or (re-search-forward subed-vtt--regexp-blank-separator nil t)
-                (goto-char (point-max)))
-            (when (re-search-backward 
subed-vtt--regexp-maybe-identifier-and-timing nil t)
-              (match-beginning 1)))))
-    (save-excursion
+      (goto-char (line-beginning-position))
       (cond
-       ((null previous-comment) nil)
-       ((null previous-timestamp)
-        (>= orig-point previous-comment))
-       ((and
-         (< previous-comment previous-timestamp)
-         (>= orig-point previous-timestamp))
+       ((and (looking-at subed-vtt--regexp-timing)
+             (looking-back subed-vtt--regexp-start-of-line))
         nil)
-       ;; there's a previous comment after a previous timestamp
-       ((< previous-timestamp previous-comment) t)
-       ((and (> previous-timestamp previous-comment)
-             (<= previous-timestamp orig-point))
+       ;; not looking right at a timestamp; check the previous line to see if 
it's blank
+       ((and
+         (looking-back subed-vtt--regexp-blank-separator)
+         (looking-at (format "[ \t]*\\(%s\n%s\\|%s\\)"
+                             subed-vtt--regexp-identifier
+                             subed-vtt--regexp-timing
+                             subed-vtt--regexp-note)))
         nil)
+       ((re-search-backward (concat "^" subed-vtt--regexp-timing) nil t) nil)
+       ((re-search-backward (concat subed-vtt--regexp-blank-separator 
subed-vtt--regexp-note) nil t) nil)
        (t t)))))
 
+(cl-defmethod subed--in-comment-p (&context (major-mode subed-vtt-mode))
+  "Return non-nil if the point is in a comment.
+Use the format-specific function for MAJOR-MODE."
+  (save-excursion
+    (goto-char (line-beginning-position))
+    (cond
+     ((looking-at subed-vtt--regexp-timing)
+      nil)
+     ((and (looking-at (format "%s\n%s"
+                               subed-vtt--regexp-identifier
+                               subed-vtt--regexp-timing))
+           (looking-back subed-vtt--regexp-blank-separator))
+      nil)
+     ;; looking at a comment
+     ((and
+       (looking-back "^[ \t]*\n\\|\\`\\(?:[ \t\n]*\\)")
+       (looking-at (concat "[ \t]*" subed-vtt--regexp-note)))
+      t)
+     ((re-search-backward (format "\\(%s%s\\)\\|%s%s"
+                                  subed-vtt--regexp-blank-separator
+                                  subed-vtt--regexp-note
+                                  subed-vtt--regexp-start-of-line
+                                  subed-vtt--regexp-timing)
+                          nil t)
+      ;;  found the note instead of the timestamp
+      (when (match-string 1)
+        t)))))
+
 (cl-defmethod subed--jump-to-subtitle-id (&context (major-mode subed-vtt-mode) 
&optional sub-id)
   "Move to the ID of a subtitle and return point.
 If SUB-ID is not given, focus the current subtitle's ID.
@@ -178,7 +174,7 @@ Use the format-specific function for MAJOR-MODE."
   (let ((orig-point (point))
         found
         (timestamp-line
-         (format "\\(?:\\`\\|\n[ \t]*\n+\\)\\(\\(%s\\)%s\\|\\(%s\\)\\)"
+         (format "\\(?:\\`\\|\n[ \t]*\n+\\)\\(\\(%s\\)\n%s\\|\\(%s\\)\\)"
                  subed-vtt--regexp-identifier
                  subed-vtt--regexp-timing
                  subed-vtt--regexp-timing)))
@@ -193,46 +189,42 @@ Use the format-specific function for MAJOR-MODE."
               (goto-char (match-beginning 2))
             (goto-char orig-point)
             nil))
-      ;; are we in a comment?
-      (cond
-       ((subed-in-header-p) nil)
-       ((subed-in-comment-p)
-        (re-search-backward subed-vtt--regexp-blank-separator nil t)
-        (when (re-search-forward subed-vtt--regexp-maybe-identifier-and-timing 
nil t)
-          (goto-char (match-beginning 1))
-          (point)))
-       (t
-        ;; we could be looking at a subtitle ID
+      (catch 'done
         (goto-char (line-beginning-position))
-        (or
-         (re-search-backward (format "\\(\\`[ \t\n]*\\|%s\\)"
-                                     subed-vtt--regexp-blank-separator)
-                             nil t)
-         (goto-char (point-min)))
         (cond
-         ((looking-at subed-vtt--regexp-maybe-identifier-and-timing)
-          (if (< orig-point (match-beginning 1))
-              ;; we are at a subtitle boundary, but before the next ID
-              ;; go backwards to find the ID
-              (when (re-search-backward 
subed-vtt--regexp-maybe-identifier-and-timing nil t)
-                (goto-char (match-beginning 1))
-                (point))
-            (goto-char (match-beginning 1))
-            (point)))
-         ((and (bobp)
-               (looking-at (format "[ \t\n]*\\(%s%s\\|%s\\)"
-                                   subed-vtt--regexp-identifier
-                                   subed-vtt--regexp-timing
-                                   subed-vtt--regexp-timing)))
-          ;; at the beginning of the buffer, looking at an ID
-          (goto-char (match-beginning 1))
-          (point))
+         ((subed-in-header-p) (throw 'done nil))
+         ;; are we looking at a timestamp? Stay here, check for ID later
+         ((looking-at subed-vtt--regexp-timing) (point))
+         ;; are we at an ID? return that
+         ((and (or (looking-back subed-vtt--regexp-blank-separator) (bobp))
+               (not (looking-at "[ \t]*\n"))
+               (not (save-excursion (re-search-forward "-->" 
(line-end-position) t)))
+               (save-excursion
+                 (forward-line)
+                 (looking-at subed-vtt--regexp-timing)))
+          (throw 'done (point)))
+         ;; are we in a comment? Search forward
+         ((subed-in-comment-p)
+          (if (re-search-forward (concat subed-vtt--regexp-start-of-line "\\(" 
subed-vtt--regexp-timing "\\)") nil t)
+              (goto-char (match-beginning 1))
+            (throw 'done nil)))
+         ;; scan backwards for a timestamp
+         ((re-search-backward (concat subed-vtt--regexp-start-of-line "\\(" 
subed-vtt--regexp-timing "\\)") nil t)
+          (goto-char (match-beginning 1))))
+        ;; We are at a timestamp; check backwards for an ID
+        (cond
+         ((bobp) (throw 'done (point)))
+         ((progn
+            (previous-line)
+            (goto-char (line-beginning-position))
+            (and (or (looking-back subed-vtt--regexp-blank-separator) (bobp))
+                 (not (looking-at "[ \t]*\n"))
+                 (save-excursion (not (re-search-forward "-->" 
(line-end-position) t)))))
+          (throw 'done (point)))
          (t
-          (or (re-search-forward subed-vtt--regexp-blank-separator nil t)
-              (goto-char (point-max)))
-          (when (re-search-backward  
subed-vtt--regexp-maybe-identifier-and-timing nil t)
-            (goto-char (match-beginning 1))
-            (point)))))))))
+          ;; go back
+          (forward-line 1)
+          (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.
@@ -271,41 +263,49 @@ 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."
   (let* ((orig-point (point))
-                                (case-fold-search nil))
-    ;; go back to the text
-    (subed-jump-to-subtitle-text sub-id)
-    (skip-chars-backward " \t\n")
-    (cond
-     ((looking-at (format "%s\\(NOTE[ \t\n]\\|%s%s\\|%s\\)"
-                          subed-vtt--regexp-blank-separator
-                          subed-vtt--regexp-identifier
-                          subed-vtt--regexp-timing
-                          subed-vtt--regexp-timing))
-      ;; empty subtitle
-      (forward-line 1)
-      (point))
-     ;; end of buffer
-     ((save-excursion (skip-chars-forward " \t\n")
-                      (eobp))
-      (forward-line 1)
-      (point))
-     (t
-      ;; scan forward each separator
-      (catch 'done
-        (while (re-search-forward subed-vtt--regexp-blank-separator nil t)
-          (goto-char (match-beginning 0))
-          (if (looking-at
-               (format "%s\\(NOTE[ \t\n]\\|%s%s\\|%s\\)"
-                       subed-vtt--regexp-blank-separator
-                       subed-vtt--regexp-identifier
-                       subed-vtt--regexp-timing
-                       subed-vtt--regexp-timing))
-              (throw 'done (point))
-            (skip-chars-forward " \t\n")))
-        (goto-char (point-max))
-        (skip-chars-backward " \t\n"))))
-    (unless (= (point) orig-point)
-      (point))))
+                                (case-fold-search nil)
+         text-point)
+    (catch 'done
+      ;; go back to the text
+      (if (subed-in-comment-p)
+          (if (re-search-forward (concat subed-vtt--regexp-start-of-line 
subed-vtt--regexp-timing) nil t)
+              (progn
+                (unless (eobp) (forward-line 1) (goto-char 
(line-beginning-position)))
+                (setq text-point (point)))
+            (throw 'done nil))
+        (setq text-point
+              (or (subed-jump-to-subtitle-text sub-id)
+                  (point))))
+      (goto-char (line-beginning-position))
+
+      (cond
+       ;; are we in the header?
+       ((subed-in-header-p)
+        (goto-char orig-point)
+        (throw 'done nil))
+       ;; are we looking at the next timing?
+       ((looking-at (concat "[ \t\n]*" subed-vtt--regexp-timing)) (point))
+       ;; are we looking at an ID and then the next timing?
+       ((looking-at (format "\\(?:[ \t]*\n\\)+%s\n%s"
+                            subed-vtt--regexp-identifier
+                            subed-vtt--regexp-timing))
+        (point))
+       ((re-search-forward (format 
"\\(%s%s\\)\\|\\(%s%s\n%s\\)\\|\\(%s%s\\)\\|%s.*-->"
+                                   subed-vtt--regexp-blank-separator
+                                   subed-vtt--regexp-note
+                                   subed-vtt--regexp-blank-separator
+                                   subed-vtt--regexp-identifier
+                                   subed-vtt--regexp-timing
+                                   subed-vtt--regexp-start-of-line
+                                   subed-vtt--regexp-timing
+                                   subed-vtt--regexp-start-of-line)
+                           nil t)
+        (goto-char (max text-point (match-beginning 0))))
+       (t (goto-char (point-max))))
+      (skip-chars-backward " \t\n")
+      (goto-char (max (point) text-point))
+      (unless (= (point) orig-point)
+        (point)))))
 
 (cl-defmethod subed--forward-subtitle-id (&context (major-mode subed-vtt-mode))
   "Move point to next subtitle's ID.
@@ -314,21 +314,15 @@ format-specific function for MAJOR-MODE."
   (let ((orig-point (point)))
     (when (subed-subtitle-id)
       (subed-jump-to-subtitle-end))
-    (unless (looking-at (format "\\(%s%s\\|%s\\)"
-                                   subed-vtt--regexp-identifier
-                                   subed-vtt--regexp-timing
-                                   subed-vtt--regexp-timing))
-      (skip-chars-backward " \n\t"))
-    (if (re-search-forward (format "%s\\(%s%s\\|%s\\)"
+    (if (re-search-forward (format "\\(?:%s\\(%s\n%s\\)\\|%s\\(%s\\)\\)"
                                    subed-vtt--regexp-blank-separator
                                    subed-vtt--regexp-identifier
                                    subed-vtt--regexp-timing
+                                   subed-vtt--regexp-start-of-line
                                    subed-vtt--regexp-timing)
                            nil t)
-        (or (subed-jump-to-subtitle-id)
-            (progn
-              (goto-char orig-point)
-              nil))
+        (goto-char (or (match-beginning 1)
+                       (match-beginning 2)))
       (goto-char orig-point)
       nil)))
 
diff --git a/tests/test-subed-vtt.el b/tests/test-subed-vtt.el
index 858f2c8904..103b128687 100644
--- a/tests/test-subed-vtt.el
+++ b/tests/test-subed-vtt.el
@@ -281,6 +281,31 @@ with multiple lines.
 This is another subtitle")
          (re-search-backward "multiple")
          (expect (subed-in-comment-p) :to-be t)))
+      (it "handles multiple blocks in a cue."
+        (with-temp-vtt-buffer
+         (insert "WEBVTT
+
+1
+00:01:01.000 --> 00:01:05.123
+Foo.
+
+NOTE
+
+This is a comment
+
+2
+00:02:02.234 --> 00:02:10.345
+
+Apparently a subtitle can have multiple comements.
+
+Bar.
+
+00:04:02.234 --> 00:04:10.345
+Baz.
+
+")
+         (re-search-backward "Bar")
+         (expect (subed-in-comment-p) :to-be nil)))
       (it "returns nil if there's a cue between the cursor and the previous 
comment."
         (with-temp-vtt-buffer
                                 (insert "WEBVTT
@@ -708,8 +733,6 @@ Bar.
                                                 (re-search-backward "OTE")
                                                 (expect 
(subed-jump-to-subtitle-start-pos) :not :to-be nil)
                                                 (expect (looking-at 
"NOTE\nThis is a comment") :to-be t))))))
-
-
     (describe "to subtitle start time"
       (it "returns start time's point if movement was successful."
         (with-temp-vtt-buffer
@@ -730,7 +753,95 @@ Bar.
       (it "returns nil if movement failed."
         (with-temp-vtt-buffer
          (expect (subed-jump-to-subtitle-time-start) :to-equal nil)))
-      )
+      (describe "when timing info doesn't have a blank line before it"
+        :var ((test-data "WEBVTT
+
+00:00:01.000 --> 00:00:01.999
+This is a test
+00:00:02.000 --> 00:00:02.999
+This is another test
+
+NOTE
+This is a comment
+with a second line.
+00:00:03.000 --> 00:00:03.999
+This is a third test.
+"))
+        (it "returns nil from the header."
+          (with-temp-vtt-buffer
+           (insert test-data)
+           (goto-char (point-min))
+           (expect (subed-jump-to-subtitle-time-start) :to-be nil)
+           (expect (point) :to-equal 1)))
+        (it "jumps to the first timing from the start of the timestamp."
+          (with-temp-vtt-buffer
+           (insert test-data)
+           (re-search-backward "00:00:01\\.000")
+           (subed-jump-to-subtitle-time-start)
+           (expect (looking-at "00:00:01") :to-be t)))
+        (it "jumps to the first timing line from the middle of the timestamp."
+          (with-temp-vtt-buffer
+           (insert test-data)
+           (goto-char (point-min))
+           (re-search-forward "--")
+           (subed-jump-to-subtitle-time-start)
+           (expect (looking-at "00:00:01") :to-be t)))
+        (it "jumps to the first timing line from the text."
+          (with-temp-vtt-buffer
+           (insert test-data)
+           (re-search-backward "This is a test")
+           (subed-jump-to-subtitle-time-start)
+           (expect (looking-at "00:00:01") :to-be t)))
+        (it "jumps to the middle timing line from the start of the timestamp."
+          (with-temp-vtt-buffer
+           (insert test-data)
+           (re-search-backward "00:00:02\\.000")
+           (subed-jump-to-subtitle-time-start)
+           (expect (looking-at "00:00:02") :to-be t)))
+        (it "jumps to the middle timing line from the middle of the timestamp."
+          (with-temp-vtt-buffer
+           (insert test-data)
+           (re-search-backward "00:00:02\\.000 --")
+           (goto-char (match-end 0))
+           (subed-jump-to-subtitle-time-start)
+           (expect (looking-at "00:00:02") :to-be t)))
+        (it "jumps to the middle timing line from the text."
+          (with-temp-vtt-buffer
+           (insert test-data)
+           (re-search-backward "another")
+           (subed-jump-to-subtitle-time-start)
+           (expect (looking-at "00:00:02") :to-be t)))
+        (it "jumps to the last timing line from the start of the comment."
+          (with-temp-vtt-buffer
+           (insert test-data)
+           (re-search-backward "NOTE")
+           (subed-jump-to-subtitle-time-start)
+           (expect (looking-at "00:00:03") :to-be t))
+          )
+        (it "jumps to the last timing line from the middle of the comment."
+          (with-temp-vtt-buffer
+           (insert test-data)
+           (re-search-backward "comment")
+           (subed-jump-to-subtitle-time-start)
+           (expect (looking-at "00:00:03") :to-be t)))
+        (it "jumps to the last timing line from the end of the comment."
+          (with-temp-vtt-buffer
+           (insert test-data)
+           (re-search-backward "second line.")
+           (goto-char (match-end 0))
+           (subed-jump-to-subtitle-time-start)
+           (expect (looking-at "00:00:03") :to-be t)))
+        (it "jumps to the last timing line from the start of the timestamp."
+          (with-temp-vtt-buffer
+           (insert test-data)
+           (re-search-backward "00:00:03\\.000")
+           (subed-jump-to-subtitle-time-start)
+           (expect (looking-at "00:00:03") :to-be t)))
+        (it "jumps to the closest timing line from the end of the file."
+          (with-temp-vtt-buffer
+           (insert test-data)
+           (subed-jump-to-subtitle-time-start)
+           (expect (looking-at "00:00:03") :to-be t)))))
     (describe "to subtitle stop time"
       (it "returns stop time's point if movement was successful."
         (with-temp-vtt-buffer
@@ -1016,7 +1127,17 @@ of multiple blocks
 
 ")
          (re-search-backward "A subtitle can")
-         (expect (subed-jump-to-subtitle-end) :to-be 82))))
+         (expect (subed-jump-to-subtitle-end) :to-be 82)))
+      (it "stops before lines that have -->."
+        (with-temp-vtt-buffer
+         (insert "WEBVTT
+
+00:00:00.000 --> 00:00:01.000
+This is a test
+a-->
+")
+         (re-search-backward "This is a test")
+         (expect (subed-jump-to-subtitle-end) :to-be 53))))
     (describe "to next subtitle ID"
       (it "returns point when there is a next subtitle."
         (with-temp-vtt-buffer
@@ -1057,7 +1178,26 @@ of multiple blocks
          (subed-jump-to-subtitle-time-stop "00:03:03.45")
          (expect (thing-at-point 'word) :to-equal "00")
          (expect (subed-forward-subtitle-id) :to-be nil)
-         (expect (thing-at-point 'word) :to-equal "00"))))
+         (expect (thing-at-point 'word) :to-equal "00")))
+      (it "finds the next subtitle timing even when there's no blank line 
before it."
+        (with-temp-vtt-buffer
+         (insert "WEBVTT
+
+00:00:01.000 --> 00:00:01.999
+This is a test
+00:00:02.000 --> 00:00:02.999
+This is another test
+
+NOTE
+This is a comment
+with a second line.
+00:00:03.000 --> 00:00:03.999
+This is a third test.
+")
+         (re-search-backward "This is a test")
+         (subed-forward-subtitle-id)
+         (expect (looking-at "00:00:02") :to-be t)
+         )))
     (describe "to previous subtitle ID"
       (it "returns point when there is a previous subtitle."
         (with-temp-vtt-buffer
@@ -2177,6 +2317,43 @@ Hello world
       (with-temp-vtt-buffer
        (expect (subed-msecs-to-timestamp 1401) :to-equal "00:00:01.401"))))
   (describe "Getting the list of subtitles"
+    (it "handles arrows and the lack of blank lines between cues."
+      (with-temp-vtt-buffer
+       ;; 
https://github.com/web-platform-tests/wpt/blob/master/webvtt/parsing/file-parsing/tests/support/arrows.vtt
+       (insert "WEBVTT
+
+-->
+00:00:00.000 --> 00:00:01.000
+text0
+foo-->
+00:00:00.000 --> 00:00:01.000
+text1
+-->foo
+00:00:00.000 --> 00:00:01.000
+text2
+--->
+00:00:00.000 --> 00:00:01.000
+text3
+-->-->
+00:00:00.000 --> 00:00:01.000
+text4
+00:00:00.000 --> 00:00:01.000
+text5
+
+00:00:00.000 -a -->
+
+00:00:00.000 --a -->
+
+00:00:00.000 - -->
+
+00:00:00.000 -- -->")
+       (let ((list (subed-subtitle-list)))
+         (expect (length list) :to-equal 6)
+         (seq-map-indexed
+          (lambda (cue i)
+            (expect (elt cue 0) :to-equal "00:00:00.000")
+            (expect (elt cue 3) :to-equal (format "text%d" i)))
+          list))))
     (it "ignores things that look like comments in cue text."
       (with-temp-vtt-buffer
        (insert "WEBVTT

Reply via email to