Serenity Operating System
at portability 148 lines 5.0 kB view raw
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}