Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/libse/Forms/FixCommonErrors/FixContinuationStyle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,22 @@ public void Fix(Subtitle subtitle, IFixCallbacks callbacks)
var inSentence = false;
bool? inItalicSentence = null;

// SanitizeString runs four regex replaces per call, and the loop sanitized every
// paragraph twice: once as pNext's text, then again as p's text one iteration later.
// Carry the sanitized "next" value forward instead. The carry is dropped whenever the
// loop writes pNext.Text below, so the following iteration re-sanitizes what the
// paragraph actually holds.
string carriedText = null;

for (var i = 0; i < subtitle.Paragraphs.Count - 1; i++)
{
var p = subtitle.Paragraphs[i];
var pNext = subtitle.Paragraphs[i + 1];
var oldText = p.Text;
var oldTextNext = pNext.Text;
var text = ContinuationUtilities.SanitizeString(p.Text);
var text = carriedText ?? ContinuationUtilities.SanitizeString(p.Text);
var textNext = ContinuationUtilities.SanitizeString(pNext.Text);
carriedText = textNext; // captured before the Arabic conversion below, which the next iteration reapplies
var isChecked = true;
var shouldProcess = true;

Expand Down Expand Up @@ -207,6 +215,7 @@ public void Fix(Subtitle subtitle, IFixCallbacks callbacks)
if (IsPreviewStep(callbacks) && isChecked || !IsPreviewStep(callbacks))
{
pNext.Text = newTextNext;
carriedText = null; // pNext.Text changed - the next iteration must re-sanitize
}

fixCount++;
Expand Down
35 changes: 35 additions & 0 deletions tests/libse/Common/UtilitiesCountTagInTextTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using Nikse.SubtitleEdit.Core.Common;

namespace LibSETests.Common;

public class UtilitiesCountTagInTextTest
{
// The char overload has two implementations: MemoryExtensions.Count on net8+ and an
// IndexOf loop on netstandard2.1. Both must agree on these, in particular on a hit that
// lands on the very last index - the loop returns early from inside its body there.
[Theory]
[InlineData("", '"', 0)]
[InlineData("no quotes here", '"', 0)]
[InlineData("\"", '"', 1)]
[InlineData("say \"this\"", '"', 2)]
[InlineData("\"\"\"", '"', 3)]
[InlineData("- No.\r\n- Then stay.", '-', 2)]
[InlineData("{\\an8}{\\pos(10,20)}Hi", '{', 2)]
[InlineData("aaa", 'a', 3)]
public void CountsEveryOccurrenceOfChar(string text, char tag, int expected)
{
Assert.Equal(expected, Utilities.CountTagInText(text, tag));
}

// The char and string overloads are separate implementations; for a single-character tag
// they must not drift apart.
[Theory]
[InlineData("\"Are you coming?\" she asked.", '"')]
[InlineData("- Yes.\r\n- No.", '-')]
[InlineData("nothing to find", 'z')]
[InlineData("trailing hit-", '-')]
public void CharOverloadAgreesWithStringOverload(string text, char tag)
{
Assert.Equal(Utilities.CountTagInText(text, tag.ToString()), Utilities.CountTagInText(text, tag));
}
}
42 changes: 42 additions & 0 deletions tests/libse/Forms/FixCommonErrors/FixContinuationStyleTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using Nikse.SubtitleEdit.Core.Common;
using Nikse.SubtitleEdit.Core.Enums;
using Nikse.SubtitleEdit.Core.Forms.FixCommonErrors;

namespace LibSETests.Forms.FixCommonErrors;

public class FixContinuationStyleTest
{
// The rule walks paragraph pairs and carries paragraph i+1's sanitized text forward to use
// as paragraph i's on the next iteration, rather than sanitizing every paragraph twice.
// Carrying the wrong side of the pair still produces the right answer on a plain
// continuation chain, so this pins a case that actually discriminates: only paragraph 2
// continues into paragraph 3 (40 ms gap), while paragraph 1 ends a sentence and paragraph 4
// is too far away (1600 ms). Get the carry wrong and paragraph 2 keeps its dots-less text.
[Fact]
public void OnlyTheContinuingParagraphGetsTrailingDots()
{
var previousStyle = Configuration.Settings.General.ContinuationStyle;
try
{
Configuration.Settings.General.ContinuationStyle = ContinuationStyle.OnlyTrailingDots;

var subtitle = new Subtitle();
subtitle.Paragraphs.Add(new Paragraph("Wait!", 0, 1122));
subtitle.Paragraphs.Add(new Paragraph("I was going to tell you", 1162, 3083));
subtitle.Paragraphs.Add(new Paragraph("and now it is too late", 3123, 4220));
subtitle.Paragraphs.Add(new Paragraph("[door slams]", 5820, 7720));
subtitle.Renumber();

new FixContinuationStyle { FixAction = "act" }.Fix(subtitle, new EmptyFixCallback());

Assert.Equal("Wait!", subtitle.Paragraphs[0].Text);
Assert.Equal("I was going to tell you...", subtitle.Paragraphs[1].Text);
Assert.Equal("and now it is too late", subtitle.Paragraphs[2].Text);
Assert.Equal("[door slams]", subtitle.Paragraphs[3].Text);
}
finally
{
Configuration.Settings.General.ContinuationStyle = previousStyle;
}
}
}
Loading