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