Serenity Operating System
1/*
2 * Copyright (c) 2020-2022, the SerenityOS developers.
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#pragma once
8
9#include <AK/Noncopyable.h>
10#include <AK/WeakPtr.h>
11#include <LibGUI/TextDocument.h>
12#include <LibGfx/Palette.h>
13#include <LibSyntax/HighlighterClient.h>
14#include <LibSyntax/Language.h>
15
16namespace Syntax {
17
18struct TextStyle {
19 const Gfx::Color color;
20 bool const bold { false };
21};
22
23class Highlighter {
24 AK_MAKE_NONCOPYABLE(Highlighter);
25 AK_MAKE_NONMOVABLE(Highlighter);
26
27public:
28 virtual ~Highlighter() = default;
29
30 virtual Language language() const = 0;
31 virtual Optional<StringView> comment_prefix() const = 0;
32 virtual Optional<StringView> comment_suffix() const = 0;
33 virtual void rehighlight(Palette const&) = 0;
34 virtual void highlight_matching_token_pair();
35
36 virtual bool is_identifier(u64) const { return false; };
37 virtual bool is_navigatable(u64) const { return false; };
38
39 void attach(HighlighterClient&);
40 void detach();
41 void cursor_did_change();
42
43 struct MatchingTokenPair {
44 u64 open;
45 u64 close;
46 };
47 Vector<MatchingTokenPair> matching_token_pairs() const;
48
49 template<typename T>
50 bool fast_is() const = delete;
51
52 // FIXME: When other syntax highlighters start using a language server, we should add a common base class here.
53 virtual bool is_cpp_semantic_highlighter() const { return false; }
54
55protected:
56 Highlighter() = default;
57
58 // FIXME: This should be WeakPtr somehow
59 HighlighterClient* m_client { nullptr };
60
61 virtual Vector<MatchingTokenPair> matching_token_pairs_impl() const = 0;
62 virtual bool token_types_equal(u64, u64) const = 0;
63 void register_nested_token_pairs(Vector<MatchingTokenPair>);
64 void clear_nested_token_pairs() { m_nested_token_pairs.clear(); }
65 size_t first_free_token_kind_serial_value() const { return m_nested_token_pairs.size(); }
66
67 struct BuddySpan {
68 int index { -1 };
69 GUI::TextDocumentSpan span_backup;
70 };
71
72 bool m_has_brace_buddies { false };
73 BuddySpan m_brace_buddies[2];
74 HashTable<MatchingTokenPair> m_nested_token_pairs;
75};
76
77class ProxyHighlighterClient final : public Syntax::HighlighterClient {
78public:
79 ProxyHighlighterClient(Syntax::HighlighterClient& client, GUI::TextPosition start, u64 nested_kind_start_value, StringView source)
80 : m_document(client.get_document())
81 , m_text(source)
82 , m_start(start)
83 , m_nested_kind_start_value(nested_kind_start_value)
84 {
85 }
86
87 Vector<GUI::TextDocumentSpan> corrected_spans() const
88 {
89 Vector<GUI::TextDocumentSpan> spans { m_spans };
90 for (auto& entry : spans) {
91 entry.range.start() = {
92 entry.range.start().line() + m_start.line(),
93 entry.range.start().line() == 0 ? entry.range.start().column() + m_start.column() : entry.range.start().column(),
94 };
95 entry.range.end() = {
96 entry.range.end().line() + m_start.line(),
97 entry.range.end().line() == 0 ? entry.range.end().column() + m_start.column() : entry.range.end().column(),
98 };
99 if (entry.data != (u64)-1)
100 entry.data += m_nested_kind_start_value;
101 }
102
103 return spans;
104 }
105
106 Vector<GUI::TextDocumentFoldingRegion> corrected_folding_regions() const
107 {
108 Vector<GUI::TextDocumentFoldingRegion> folding_regions { m_folding_regions };
109 for (auto& entry : folding_regions) {
110 entry.range.start() = {
111 entry.range.start().line() + m_start.line(),
112 entry.range.start().line() == 0 ? entry.range.start().column() + m_start.column() : entry.range.start().column(),
113 };
114 entry.range.end() = {
115 entry.range.end().line() + m_start.line(),
116 entry.range.end().line() == 0 ? entry.range.end().column() + m_start.column() : entry.range.end().column(),
117 };
118 }
119
120 return folding_regions;
121 }
122
123 Vector<Syntax::Highlighter::MatchingTokenPair> corrected_token_pairs(Vector<Syntax::Highlighter::MatchingTokenPair> pairs) const
124 {
125 for (auto& pair : pairs) {
126 pair.close += m_nested_kind_start_value;
127 pair.open += m_nested_kind_start_value;
128 }
129 return pairs;
130 }
131
132private:
133 virtual Vector<GUI::TextDocumentSpan>& spans() override { return m_spans; }
134 virtual Vector<GUI::TextDocumentSpan> const& spans() const override { return m_spans; }
135 virtual void set_span_at_index(size_t index, GUI::TextDocumentSpan span) override { m_spans.at(index) = move(span); }
136
137 virtual Vector<GUI::TextDocumentFoldingRegion>& folding_regions() override { return m_folding_regions; }
138 virtual Vector<GUI::TextDocumentFoldingRegion> const& folding_regions() const override { return m_folding_regions; }
139
140 virtual DeprecatedString highlighter_did_request_text() const override { return m_text; }
141 virtual void highlighter_did_request_update() override { }
142 virtual GUI::TextDocument& highlighter_did_request_document() override { return m_document; }
143 virtual GUI::TextPosition highlighter_did_request_cursor() const override { return {}; }
144 virtual void highlighter_did_set_spans(Vector<GUI::TextDocumentSpan> spans) override { m_spans = move(spans); }
145 virtual void highlighter_did_set_folding_regions(Vector<GUI::TextDocumentFoldingRegion> folding_regions) override { m_folding_regions = folding_regions; }
146
147 Vector<GUI::TextDocumentSpan> m_spans;
148 Vector<GUI::TextDocumentFoldingRegion> m_folding_regions;
149 GUI::TextDocument& m_document;
150 StringView m_text;
151 GUI::TextPosition m_start;
152 u64 m_nested_kind_start_value { 0 };
153};
154
155}
156
157template<>
158struct AK::Traits<Syntax::Highlighter::MatchingTokenPair> : public AK::GenericTraits<Syntax::Highlighter::MatchingTokenPair> {
159 static unsigned hash(Syntax::Highlighter::MatchingTokenPair const& pair)
160 {
161 return pair_int_hash(u64_hash(pair.open), u64_hash(pair.close));
162 }
163 static bool equals(Syntax::Highlighter::MatchingTokenPair const& a, Syntax::Highlighter::MatchingTokenPair const& b)
164 {
165 return a.open == b.open && a.close == b.close;
166 }
167};