diff --git a/src/emitter.cpp b/src/emitter.cpp index e57ee4684..b30f4d2c1 100644 --- a/src/emitter.cpp +++ b/src/emitter.cpp @@ -751,6 +751,7 @@ Emitter& Emitter::Write(const char* str, std::size_t size) { case StringFormat::Literal: Utils::WriteLiteralString(m_stream, str, size, m_pState->CurIndent() + m_pState->GetIndent()); + m_pState->SetLastLiteralEnd(m_stream.pos()); break; } @@ -948,6 +949,9 @@ Emitter& Emitter::Write(const _Comment& comment) { if (!good()) return *this; + if (m_stream.pos() > 0 && m_stream.pos() == m_pState->LastLiteralEnd()) + m_stream << "\n"; + PrepareNode(EmitterNodeType::NoType); if (m_stream.col() == 0 && @@ -1007,6 +1011,7 @@ Emitter& Emitter::Write(const Binary& binary) { Utils::WriteLiteralBinary(m_stream, binary, m_pState->CurIndent() + m_pState->GetIndent(), m_pState->GetWrap()); + m_pState->SetLastLiteralEnd(m_stream.pos()); break; } diff --git a/src/emitterstate.cpp b/src/emitterstate.cpp index b04951e5e..abc0fbe62 100644 --- a/src/emitterstate.cpp +++ b/src/emitterstate.cpp @@ -34,7 +34,8 @@ EmitterState::EmitterState() m_hasAlias(false), m_hasTag(false), m_hasNonContent(false), - m_docCount(0) {} + m_docCount(0), + m_lastLiteralEnd(0) {} EmitterState::~EmitterState() = default; diff --git a/src/emitterstate.h b/src/emitterstate.h index 896134956..cc81c45ef 100644 --- a/src/emitterstate.h +++ b/src/emitterstate.h @@ -72,6 +72,8 @@ class EmitterState { return m_hasAnchor || m_hasTag || m_hasNonContent; } bool HasBegunContent() const { return m_hasAnchor || m_hasTag; } + void SetLastLiteralEnd(std::size_t pos) { m_lastLiteralEnd = pos; } + std::size_t LastLiteralEnd() const { return m_lastLiteralEnd; } void ClearModifiedSettings(); void RestoreGlobalModifiedSettings(); @@ -200,6 +202,7 @@ class EmitterState { bool m_hasTag; bool m_hasNonContent; std::size_t m_docCount; + std::size_t m_lastLiteralEnd; }; template diff --git a/test/integration/emitter_test.cpp b/test/integration/emitter_test.cpp index ddf0a4068..35727cbdd 100644 --- a/test/integration/emitter_test.cpp +++ b/test/integration/emitter_test.cpp @@ -454,6 +454,76 @@ TEST_F(EmitterTest, LiteralWithAndWithoutTrailingEmptyLines) { "- something"); } +TEST_F(EmitterTest, CommentAfterLiteral) { + out << Literal << "Hello\nworld" << Comment("A comment"); + + ExpectEmit("|-\n Hello\n world\n# A comment"); + EXPECT_EQ("Hello\nworld", Load(out.c_str()).as()); +} + +TEST_F(EmitterTest, CommentAfterLiteralPreservesTrailingNewlines) { + out << BeginSeq; + out << Literal << "A\nB\n" << Comment("clip"); + out << Literal << "A\nB\n\n\n" << Comment("keep"); + out << EndSeq; + + ExpectEmit("- |\n A\n B\n# clip\n- |+\n A\n B\n\n\n# keep"); + const Node parsed = Load(out.c_str()); + EXPECT_EQ("A\nB\n", parsed[0].as()); + EXPECT_EQ("A\nB\n\n\n", parsed[1].as()); +} + +TEST_F(EmitterTest, CommentAfterLiteralWithExplicitNewline) { + out << Literal << "A\n\n" << Newline << Comment("A comment"); + + ExpectEmit("|+\n A\n\n# A comment"); + EXPECT_EQ("A\n\n", Load(out.c_str()).as()); +} + +TEST_F(EmitterTest, CommentAfterLiteralInNestedMap) { + out << BeginMap << Key << "outer" << Value << BeginMap; + out << Key << "literal" << Value << Literal << "Hello\nworld"; + out << Comment("A comment"); + out << Key << "plain" << Value << "text" << Comment("inline comment"); + out << EndMap << EndMap; + + ExpectEmit( + "outer:\n" + " literal: |-\n" + " Hello\n" + " world\n" + " # A comment\n" + " plain: text # inline comment"); + const Node parsed = Load(out.c_str()); + EXPECT_EQ("Hello\nworld", parsed["outer"]["literal"].as()); + EXPECT_EQ("text", parsed["outer"]["plain"].as()); +} + +TEST_F(EmitterTest, CommentAfterLiteralKey) { + out << BeginMap << Key << Literal << "Hello\nworld" << Comment("A comment"); + out << Value << "value" << EndMap; + + ExpectEmit("? |-\n Hello\n world\n# A comment\n: value"); + EXPECT_EQ("value", Load(out.c_str())["Hello\nworld"].as()); +} + +TEST_F(EmitterTest, CommentAfterLiteralBinary) { + const unsigned char data[] = {'H', 'e', 'l', 'l', 'o'}; + out << Literal << Binary(data, sizeof(data)) << Comment("A comment"); + + ExpectEmit("!!binary |-\n SGVsbG8=\n# A comment"); + EXPECT_EQ(Binary(data, sizeof(data)), Load(out.c_str()).as()); +} + +TEST_F(EmitterTest, CommentAfterLiteralInOutputStream) { + std::ostringstream stream; + Emitter emitter(stream); + emitter << Literal << "Hello\nworld" << Comment("A comment"); + + EXPECT_TRUE(emitter.good()); + EXPECT_EQ("|-\n Hello\n world\n# A comment", stream.str()); + EXPECT_EQ("Hello\nworld", Load(stream.str()).as()); +} TEST_F(EmitterTest, AutoLongKeyScalar) { out << BeginMap;