Index: lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.cpp =================================================================== --- lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.cpp +++ lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.cpp @@ -139,6 +139,16 @@ FileManager file_mgr(file_opts, FileSystem::Instance().GetVirtualFileSystem()); + // The line might end in a backslash which would cause Clang to drop the + // backslash and the terminating line feed. This makes sense when parsing C++, + // but when highlighting we care about preserving the backslash/newline. To + // not lose this information we remove the line feed here so that Clang knows + // this is just a single line we are highlighting. We add back the newline + // after tokenizing. + const bool line_had_cr_lf = line.endswith("\r\n"); + const bool line_had_lf = line.endswith("\n") && !line_had_cr_lf; + line = line.trim("\r\n"); + unsigned line_number = previous_lines.count('\n') + 1U; // Let's build the actual source code Clang needs and setup some utility @@ -227,6 +237,11 @@ color.Apply(result, to_print); } + if (line_had_cr_lf) + result << "\r\n"; + else if (line_had_lf) + result << "\n"; + // If we went over the whole file but couldn't find our own file, then // somehow our setup was wrong. When we're in release mode we just give the // user the normal line and pretend we don't know how to highlight it. In Index: lldb/unittests/Language/Highlighting/HighlighterTest.cpp =================================================================== --- lldb/unittests/Language/Highlighting/HighlighterTest.cpp +++ lldb/unittests/Language/Highlighting/HighlighterTest.cpp @@ -205,6 +205,43 @@ highlightC("#include \"foo\" //c", s)); } +TEST_F(HighlighterTest, ClangPreserveNewLine) { + HighlightStyle s; + s.comment.Set("", ""); + + EXPECT_EQ("//\n", highlightC("//\n", s)); +} + +TEST_F(HighlighterTest, ClangTrailingBackslashBeforeNewline) { + HighlightStyle s; + + EXPECT_EQ("\\\n", highlightC("\\\n", s)); + EXPECT_EQ("\\\r\n", highlightC("\\\r\n", s)); + + EXPECT_EQ("#define a \\\n", highlightC("#define a \\\n", s)); + EXPECT_EQ("#define a \\\r\n", highlightC("#define a \\\r\n", s)); +} + +TEST_F(HighlighterTest, ClangTrailingBackslashWithWhitespace) { + HighlightStyle s; + + EXPECT_EQ("\\ \n", highlightC("\\ \n", s)); + EXPECT_EQ("\\ \t\n", highlightC("\\ \t\n", s)); + EXPECT_EQ("\\ \n", highlightC("\\ \n", s)); + EXPECT_EQ("\\\t\n", highlightC("\\\t\n", s)); + + EXPECT_EQ("#define a \\ \n", highlightC("#define a \\ \n", s)); + EXPECT_EQ("#define a \\ \t\n", highlightC("#define a \\ \t\n", s)); + EXPECT_EQ("#define a \\ \n", highlightC("#define a \\ \n", s)); + EXPECT_EQ("#define a \\\t\n", highlightC("#define a \\\t\n", s)); +} + +TEST_F(HighlighterTest, ClangTrailingBackslashMissingNewLine) { + HighlightStyle s; + EXPECT_EQ("\\", highlightC("\\", s)); + EXPECT_EQ("#define a\\", highlightC("#define a\\", s)); +} + TEST_F(HighlighterTest, ClangComments) { HighlightStyle s; s.comment.Set("", "");