Serenity Operating System
1/*
2 * Copyright (c) 2020-2022, the SerenityOS developers.
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <AK/Debug.h>
8#include <LibCpp/Lexer.h>
9#include <LibCpp/SyntaxHighlighter.h>
10#include <LibGUI/TextEditor.h>
11#include <LibGfx/Font/Font.h>
12#include <LibGfx/Palette.h>
13
14namespace Cpp {
15
16static Syntax::TextStyle style_for_token_type(Gfx::Palette const& palette, Cpp::Token::Type type)
17{
18 switch (type) {
19 case Cpp::Token::Type::Keyword:
20 return { palette.syntax_keyword(), true };
21 case Cpp::Token::Type::KnownType:
22 return { palette.syntax_type(), true };
23 case Cpp::Token::Type::Identifier:
24 return { palette.syntax_identifier(), false };
25 case Cpp::Token::Type::DoubleQuotedString:
26 case Cpp::Token::Type::SingleQuotedString:
27 case Cpp::Token::Type::RawString:
28 return { palette.syntax_string(), false };
29 case Cpp::Token::Type::Integer:
30 case Cpp::Token::Type::Float:
31 return { palette.syntax_number(), false };
32 case Cpp::Token::Type::IncludePath:
33 return { palette.syntax_preprocessor_value(), false };
34 case Cpp::Token::Type::EscapeSequence:
35 return { palette.syntax_keyword(), true };
36 case Cpp::Token::Type::PreprocessorStatement:
37 case Cpp::Token::Type::IncludeStatement:
38 return { palette.syntax_preprocessor_statement(), false };
39 case Cpp::Token::Type::Comment:
40 return { palette.syntax_comment(), false };
41 default:
42 return { palette.base_text(), false };
43 }
44}
45
46bool SyntaxHighlighter::is_identifier(u64 token) const
47{
48 auto cpp_token = static_cast<Cpp::Token::Type>(token);
49 return cpp_token == Cpp::Token::Type::Identifier;
50}
51
52bool SyntaxHighlighter::is_navigatable(u64 token) const
53{
54 auto cpp_token = static_cast<Cpp::Token::Type>(token);
55 return cpp_token == Cpp::Token::Type::IncludePath;
56}
57
58void SyntaxHighlighter::rehighlight(Palette const& palette)
59{
60 auto text = m_client->get_text();
61 Cpp::Lexer lexer(text);
62
63 Vector<Token> folding_region_start_tokens;
64 Vector<GUI::TextDocumentFoldingRegion> folding_regions;
65 Vector<GUI::TextDocumentSpan> spans;
66 lexer.lex_iterable([&](auto token) {
67 // FIXME: The +1 for the token end column is a quick hack due to not wanting to modify the lexer (which is also used by the parser). Maybe there's a better way to do this.
68 dbgln_if(SYNTAX_HIGHLIGHTING_DEBUG, "{} @ {}:{} - {}:{}", token.type_as_deprecated_string(), token.start().line, token.start().column, token.end().line, token.end().column + 1);
69 GUI::TextDocumentSpan span;
70 span.range.set_start({ token.start().line, token.start().column });
71 span.range.set_end({ token.end().line, token.end().column + 1 });
72 auto style = style_for_token_type(palette, token.type());
73 span.attributes.color = style.color;
74 span.attributes.bold = style.bold;
75 span.is_skippable = token.type() == Cpp::Token::Type::Whitespace;
76 span.data = static_cast<u64>(token.type());
77 spans.append(span);
78
79 if (token.type() == Token::Type::LeftCurly) {
80 folding_region_start_tokens.append(token);
81 } else if (token.type() == Token::Type::RightCurly) {
82 if (!folding_region_start_tokens.is_empty()) {
83 auto start_token = folding_region_start_tokens.take_last();
84 GUI::TextDocumentFoldingRegion folding_region;
85 folding_region.range.set_start({ start_token.end().line, start_token.end().column });
86 folding_region.range.set_end({ token.start().line, token.start().column });
87 folding_regions.append(move(folding_region));
88 }
89 }
90 });
91 m_client->do_set_spans(move(spans));
92 m_client->do_set_folding_regions(move(folding_regions));
93
94 m_has_brace_buddies = false;
95 highlight_matching_token_pair();
96
97 m_client->do_update();
98}
99
100Vector<SyntaxHighlighter::MatchingTokenPair> SyntaxHighlighter::matching_token_pairs_impl() const
101{
102 static Vector<SyntaxHighlighter::MatchingTokenPair> pairs;
103 if (pairs.is_empty()) {
104 pairs.append({ static_cast<u64>(Cpp::Token::Type::LeftCurly), static_cast<u64>(Cpp::Token::Type::RightCurly) });
105 pairs.append({ static_cast<u64>(Cpp::Token::Type::LeftParen), static_cast<u64>(Cpp::Token::Type::RightParen) });
106 pairs.append({ static_cast<u64>(Cpp::Token::Type::LeftBracket), static_cast<u64>(Cpp::Token::Type::RightBracket) });
107 }
108 return pairs;
109}
110
111bool SyntaxHighlighter::token_types_equal(u64 token1, u64 token2) const
112{
113 return static_cast<Cpp::Token::Type>(token1) == static_cast<Cpp::Token::Type>(token2);
114}
115
116}