CppSyntaxHighlighter.cpp 5.9 KB

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