Serenity Operating System
1#include <LibGUI/CppLexer.h>
2#include <LibGUI/CppSyntaxHighlighter.h>
3#include <LibGUI/TextEditor.h>
4#include <LibGfx/Font.h>
5
6namespace GUI {
7
8struct TextStyle {
9 Color color;
10 const Gfx::Font* font { nullptr };
11};
12
13static TextStyle style_for_token_type(CppToken::Type type)
14{
15 switch (type) {
16 case CppToken::Type::Keyword:
17 return { Color::Black, &Gfx::Font::default_bold_fixed_width_font() };
18 case CppToken::Type::KnownType:
19 return { Color::from_rgb(0x800080), &Gfx::Font::default_bold_fixed_width_font() };
20 case CppToken::Type::Identifier:
21 return { Color::from_rgb(0x092e64) };
22 case CppToken::Type::DoubleQuotedString:
23 case CppToken::Type::SingleQuotedString:
24 case CppToken::Type::Number:
25 return { Color::from_rgb(0x800000) };
26 case CppToken::Type::PreprocessorStatement:
27 return { Color::from_rgb(0x008080) };
28 case CppToken::Type::Comment:
29 return { Color::from_rgb(0x008000) };
30 default:
31 return { Color::Black };
32 }
33}
34
35void CppSyntaxHighlighter::rehighlight()
36{
37 ASSERT(m_editor);
38 auto text = m_editor->text();
39 CppLexer lexer(text);
40 auto tokens = lexer.lex();
41
42 Vector<GUI::TextDocumentSpan> spans;
43 for (auto& token : tokens) {
44#ifdef DEBUG_SYNTAX_HIGHLIGHTING
45 dbg() << token.to_string() << " @ " << token.m_start.line << ":" << token.m_start.column << " - " << token.m_end.line << ":" << token.m_end.column;
46#endif
47 GUI::TextDocumentSpan span;
48 span.range.set_start({ token.m_start.line, token.m_start.column });
49 span.range.set_end({ token.m_end.line, token.m_end.column });
50 auto style = style_for_token_type(token.m_type);
51 span.color = style.color;
52 span.font = style.font;
53 span.is_skippable = token.m_type == CppToken::Type::Whitespace;
54 span.data = (void*)token.m_type;
55 spans.append(span);
56 }
57 m_editor->document().set_spans(spans);
58
59 m_has_brace_buddies = false;
60 highlight_matching_token_pair();
61
62 m_editor->update();
63}
64
65void CppSyntaxHighlighter::highlight_matching_token_pair()
66{
67 ASSERT(m_editor);
68 auto& document = m_editor->document();
69
70 enum class Direction {
71 Forward,
72 Backward,
73 };
74
75 auto find_span_of_type = [&](auto i, CppToken::Type type, CppToken::Type not_type, Direction direction) -> Optional<size_t> {
76 size_t nesting_level = 0;
77 bool forward = direction == Direction::Forward;
78 for (forward ? ++i : --i; forward ? (i < document.spans().size()) : (i >= 0); forward ? ++i : --i) {
79 auto& span = document.spans().at(i);
80 auto span_token_type = (CppToken::Type)((uintptr_t)span.data);
81 if (span_token_type == not_type) {
82 ++nesting_level;
83 } else if (span_token_type == type) {
84 if (nesting_level-- <= 0)
85 return i;
86 }
87 }
88 return {};
89 };
90
91 auto make_buddies = [&](int index0, int index1) {
92 auto& buddy0 = const_cast<GUI::TextDocumentSpan&>(document.spans()[index0]);
93 auto& buddy1 = const_cast<GUI::TextDocumentSpan&>(document.spans()[index1]);
94 m_has_brace_buddies = true;
95 m_brace_buddies[0].index = index0;
96 m_brace_buddies[1].index = index1;
97 m_brace_buddies[0].span_backup = buddy0;
98 m_brace_buddies[1].span_backup = buddy1;
99 buddy0.background_color = Color::DarkCyan;
100 buddy1.background_color = Color::DarkCyan;
101 buddy0.color = Color::White;
102 buddy1.color = Color::White;
103 m_editor->update();
104 };
105
106 struct MatchingTokenPair {
107 CppToken::Type open;
108 CppToken::Type close;
109 };
110
111 MatchingTokenPair pairs[] = {
112 { CppToken::Type::LeftCurly, CppToken::Type::RightCurly },
113 { CppToken::Type::LeftParen, CppToken::Type::RightParen },
114 { CppToken::Type::LeftBracket, CppToken::Type::RightBracket },
115 };
116
117 for (size_t i = 0; i < document.spans().size(); ++i) {
118 auto& span = const_cast<GUI::TextDocumentSpan&>(document.spans().at(i));
119 auto token_type = (CppToken::Type)((uintptr_t)span.data);
120
121 for (auto& pair : pairs) {
122 if (token_type == pair.open && span.range.start() == m_editor->cursor()) {
123 auto buddy = find_span_of_type(i, pair.close, pair.open, Direction::Forward);
124 if (buddy.has_value())
125 make_buddies(i, buddy.value());
126 return;
127 }
128 }
129
130 auto right_of_end = span.range.end();
131 right_of_end.set_column(right_of_end.column() + 1);
132
133 for (auto& pair : pairs) {
134 if (token_type == pair.close && right_of_end == m_editor->cursor()) {
135 auto buddy = find_span_of_type(i, pair.open, pair.close, Direction::Backward);
136 if (buddy.has_value())
137 make_buddies(i, buddy.value());
138 return;
139 }
140 }
141 }
142}
143
144CppSyntaxHighlighter::~CppSyntaxHighlighter()
145{
146}
147
148}