diff --git a/Libraries/LibWebView/SourceHighlighter.cpp b/Libraries/LibWebView/SourceHighlighter.cpp index f4ab5b5f96f6..f21c52a2ac38 100644 --- a/Libraries/LibWebView/SourceHighlighter.cpp +++ b/Libraries/LibWebView/SourceHighlighter.cpp @@ -17,12 +17,45 @@ namespace WebView { -SourceDocument::SourceDocument(StringView source) - : m_source(source) +SourceDocument::SourceDocument(String const& source) { - m_source.for_each_split_view('\n', AK::SplitBehavior::KeepEmpty, [&](auto line) { - m_lines.append(Syntax::TextDocumentLine { *this, line }); - }); + // HTML, CSS and JS differ slightly on what they consider a newline to be. + // In order to make them get along in documents that include a mix of the three, process the source to make the + // newlines consistent before doing any highlighting. + + // Optimization: If all the newlines are \n, just use the input string. + if (!source.code_points().contains_any_of(Array { '\r', 0x2028, 0x2029 })) { + m_source = source; + } else { + StringBuilder builder { source.byte_count() }; + // Convert any '\r\n', \r, or to \n + bool previous_was_cr = false; + for (u32 code_point : source.code_points()) { + if (previous_was_cr && code_point != '\n') + builder.append('\n'); + previous_was_cr = false; + + switch (code_point) { + case '\r': + previous_was_cr = true; + break; + case JS::LINE_SEPARATOR: + case JS::PARAGRAPH_SEPARATOR: + builder.append('\n'); + break; + default: + builder.append_code_point(code_point); + } + } + m_source = builder.to_string_without_validation(); + } + + m_source.code_points().for_each_split_view( + [](u32 it) { return it == '\n'; }, + SplitBehavior::KeepEmpty, + [&](auto line) { + m_lines.append(Syntax::TextDocumentLine { *this, line.as_string() }); + }); } Syntax::TextDocumentLine& SourceDocument::line(size_t line_index) @@ -35,7 +68,7 @@ Syntax::TextDocumentLine const& SourceDocument::line(size_t line_index) const return m_lines[line_index]; } -SourceHighlighterClient::SourceHighlighterClient(StringView source, Syntax::Language language) +SourceHighlighterClient::SourceHighlighterClient(String const& source, Syntax::Language language) : m_document(SourceDocument::create(source)) { // HACK: Syntax highlighters require a palette, but we don't actually care about the output styling, only the type of token for each span. @@ -114,7 +147,7 @@ void SourceHighlighterClient::highlighter_did_set_folding_regions(Vector create(StringView source) + static NonnullRefPtr create(String const& source) { return adopt_ref(*new (nothrow) SourceDocument(source)); } @@ -38,18 +38,18 @@ class SourceDocument final : public Syntax::Document { virtual Syntax::TextDocumentLine& line(size_t line_index) override; private: - SourceDocument(StringView source); + SourceDocument(String const& source); // ^ Syntax::Document virtual void update_views(Badge) override { } - StringView m_source; + String m_source; Vector m_lines; }; class SourceHighlighterClient final : public Syntax::HighlighterClient { public: - SourceHighlighterClient(StringView source, Syntax::Language); + SourceHighlighterClient(String const& source, Syntax::Language); virtual ~SourceHighlighterClient() = default; String to_html_string(URL::URL const& url, URL::URL const& base_url, HighlightOutputMode) const; @@ -75,7 +75,7 @@ class SourceHighlighterClient final : public Syntax::HighlighterClient { OwnPtr m_highlighter; }; -String highlight_source(URL::URL const& url, URL::URL const& base_url, StringView, Syntax::Language, HighlightOutputMode); +String highlight_source(URL::URL const& url, URL::URL const& base_url, String const& source, Syntax::Language, HighlightOutputMode); constexpr inline StringView HTML_HIGHLIGHTER_STYLE = R"~~~( @media (prefers-color-scheme: dark) {