/* * Copyright (c) 2020-2022, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include namespace Syntax { enum class Language { Cpp, CSS, GitCommit, GML, HTML, INI, JavaScript, PlainText, SQL, Shell, }; struct TextStyle { const Gfx::Color color; bool const bold { false }; }; StringView language_to_string(Language); StringView common_language_extension(Language); class Highlighter { AK_MAKE_NONCOPYABLE(Highlighter); AK_MAKE_NONMOVABLE(Highlighter); public: virtual ~Highlighter() = default; virtual Language language() const = 0; virtual Optional comment_prefix() const = 0; virtual Optional comment_suffix() const = 0; virtual void rehighlight(Palette const&) = 0; virtual void highlight_matching_token_pair(); virtual bool is_identifier(u64) const { return false; }; virtual bool is_navigatable(u64) const { return false; }; void attach(HighlighterClient&); void detach(); void cursor_did_change(); struct MatchingTokenPair { u64 open; u64 close; }; Vector matching_token_pairs() const; template bool fast_is() const = delete; // FIXME: When other syntax highlighters start using a language server, we should add a common base class here. virtual bool is_cpp_semantic_highlighter() const { return false; } protected: Highlighter() = default; // FIXME: This should be WeakPtr somehow HighlighterClient* m_client { nullptr }; virtual Vector matching_token_pairs_impl() const = 0; virtual bool token_types_equal(u64, u64) const = 0; void register_nested_token_pairs(Vector); void clear_nested_token_pairs() { m_nested_token_pairs.clear(); } size_t first_free_token_kind_serial_value() const { return m_nested_token_pairs.size(); } struct BuddySpan { int index { -1 }; GUI::TextDocumentSpan span_backup; }; bool m_has_brace_buddies { false }; BuddySpan m_brace_buddies[2]; HashTable m_nested_token_pairs; }; class ProxyHighlighterClient final : public Syntax::HighlighterClient { public: ProxyHighlighterClient(Syntax::HighlighterClient& client, GUI::TextPosition start, u64 nested_kind_start_value, StringView source) : m_document(client.get_document()) , m_text(source) , m_start(start) , m_nested_kind_start_value(nested_kind_start_value) { } Vector corrected_spans() const { Vector spans { m_spans }; for (auto& entry : spans) { entry.range.start() = { entry.range.start().line() + m_start.line(), entry.range.start().line() == 0 ? entry.range.start().column() + m_start.column() : entry.range.start().column(), }; entry.range.end() = { entry.range.end().line() + m_start.line(), entry.range.end().line() == 0 ? entry.range.end().column() + m_start.column() : entry.range.end().column(), }; if (entry.data != (u64)-1) entry.data += m_nested_kind_start_value; } return spans; } Vector corrected_token_pairs(Vector pairs) const { for (auto& pair : pairs) { pair.close += m_nested_kind_start_value; pair.open += m_nested_kind_start_value; } return pairs; } private: virtual Vector& spans() override { return m_spans; } virtual Vector const& spans() const override { return m_spans; } virtual void set_span_at_index(size_t index, GUI::TextDocumentSpan span) override { m_spans.at(index) = move(span); } virtual DeprecatedString highlighter_did_request_text() const override { return m_text; } virtual void highlighter_did_request_update() override { } virtual GUI::TextDocument& highlighter_did_request_document() override { return m_document; } virtual GUI::TextPosition highlighter_did_request_cursor() const override { return {}; } virtual void highlighter_did_set_spans(Vector spans) override { m_spans = move(spans); } Vector m_spans; GUI::TextDocument& m_document; StringView m_text; GUI::TextPosition m_start; u64 m_nested_kind_start_value { 0 }; }; } template<> struct AK::Traits : public AK::GenericTraits { static unsigned hash(Syntax::Highlighter::MatchingTokenPair const& pair) { return pair_int_hash(u64_hash(pair.open), u64_hash(pair.close)); } static bool equals(Syntax::Highlighter::MatchingTokenPair const& a, Syntax::Highlighter::MatchingTokenPair const& b) { return a.open == b.open && a.close == b.close; } };