/* * Copyright (c) 2023, Tim Flynn * Copyright (c) 2024, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include namespace WebView { enum class HighlightOutputMode { FullDocument, // Include HTML header, title, style sheet, etc SourceOnly, // Just the highlighted source }; class SourceDocument final : public Syntax::Document { public: static NonnullRefPtr create(String const& source) { return adopt_ref(*new (nothrow) SourceDocument(source)); } virtual ~SourceDocument() = default; StringView text() const { return m_source; } size_t line_count() const { return m_lines.size(); } // ^ Syntax::Document virtual Syntax::TextDocumentLine const& line(size_t line_index) const override; virtual Syntax::TextDocumentLine& line(size_t line_index) override; private: SourceDocument(String const& source); // ^ Syntax::Document virtual void update_views(Badge) override { } String m_source; Vector m_lines; }; class SourceHighlighterClient final : public Syntax::HighlighterClient { public: SourceHighlighterClient(String const& source, Syntax::Language); virtual ~SourceHighlighterClient() = default; String to_html_string(Optional const&, URL::URL const& base_url, HighlightOutputMode) const; private: // ^ Syntax::HighlighterClient virtual Vector const& spans() const override; virtual void set_span_at_index(size_t index, Syntax::TextDocumentSpan span) override; virtual Vector& folding_regions() override; virtual Vector const& folding_regions() const override; virtual ByteString highlighter_did_request_text() const override; virtual void highlighter_did_request_update() override; virtual Syntax::Document& highlighter_did_request_document() override; virtual Syntax::TextPosition highlighter_did_request_cursor() const override; virtual void highlighter_did_set_spans(Vector) override; virtual void highlighter_did_set_folding_regions(Vector) override; StringView class_for_token(u64 token_type) const; SourceDocument& document() const { return *m_document; } NonnullRefPtr m_document; OwnPtr m_highlighter; }; String highlight_source(Optional const&, URL::URL const& base_url, String const& source, Syntax::Language, HighlightOutputMode); constexpr inline StringView HTML_HIGHLIGHTER_STYLE = R"~~~( @media (prefers-color-scheme: dark) { :root { --comment-color: lightgreen; --keyword-color: orangered; --name-color: orange; --value-color: deepskyblue; --internal-color: darkgrey; --string-color: goldenrod; --error-color: red; --line-number-color: darkgrey; } } @media (prefers-color-scheme: light) { :root { --comment-color: green; --keyword-color: red; --name-color: darkorange; --value-color: blue; --internal-color: dimgrey; --string-color: darkgoldenrod; --error-color: darkred; --line-number-color: dimgrey; } } html { color-scheme: light dark; } .html { font-size: 10pt; font-family: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; } .line { counter-increment: line; white-space: pre; } .line::before { content: counter(line) " "; display: inline-block; width: 2.5em; padding-right: 0.5em; text-align: right; color: var(--line-number-color); } .tag { font-weight: 600; color: var(--keyword-color); } .comment { color: var(--comment-color); } .attribute-name { color: var(--name-color); } .attribute-value { color: var(--value-color); } .internal { color: var(--internal-color); } .invalid { color: var(--error-color); text-decoration: currentColor wavy underline; } .at-keyword, .function, .keyword, .control-keyword, .url { color: var(--keyword-color); } .number, .hash { color: var(--value-color); } .string { color: var(--string-color); } )~~~"sv; }