CppSyntaxHighlighter.cpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  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. void CppSyntaxHighlighter::rehighlight()
  37. {
  38. ASSERT(m_editor);
  39. auto text = m_editor->text();
  40. CppLexer lexer(text);
  41. auto tokens = lexer.lex();
  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 = reinterpret_cast<void*>(token.m_type);
  55. spans.append(span);
  56. }
  57. m_editor->document().set_spans(spans);
  58. m_has_brace_buddies = false;
  59. highlight_matching_token_pair();
  60. m_editor->update();
  61. }
  62. void CppSyntaxHighlighter::highlight_matching_token_pair()
  63. {
  64. ASSERT(m_editor);
  65. auto& document = m_editor->document();
  66. enum class Direction {
  67. Forward,
  68. Backward,
  69. };
  70. auto find_span_of_type = [&](auto i, CppToken::Type type, CppToken::Type not_type, Direction direction) -> Optional<size_t> {
  71. size_t nesting_level = 0;
  72. bool forward = direction == Direction::Forward;
  73. if (forward) {
  74. ++i;
  75. if (i >= document.spans().size())
  76. return {};
  77. } else {
  78. if (i == 0)
  79. return {};
  80. --i;
  81. }
  82. for (;;) {
  83. auto& span = document.spans().at(i);
  84. auto span_token_type = (CppToken::Type)((FlatPtr)span.data);
  85. if (span_token_type == not_type) {
  86. ++nesting_level;
  87. } else if (span_token_type == type) {
  88. if (nesting_level-- <= 0)
  89. return i;
  90. }
  91. if (forward) {
  92. ++i;
  93. if (i >= document.spans().size())
  94. return {};
  95. } else {
  96. if (i == 0)
  97. return {};
  98. --i;
  99. }
  100. }
  101. return {};
  102. };
  103. auto make_buddies = [&](int index0, int index1) {
  104. auto& buddy0 = const_cast<GUI::TextDocumentSpan&>(document.spans()[index0]);
  105. auto& buddy1 = const_cast<GUI::TextDocumentSpan&>(document.spans()[index1]);
  106. m_has_brace_buddies = true;
  107. m_brace_buddies[0].index = index0;
  108. m_brace_buddies[1].index = index1;
  109. m_brace_buddies[0].span_backup = buddy0;
  110. m_brace_buddies[1].span_backup = buddy1;
  111. buddy0.background_color = Color::DarkCyan;
  112. buddy1.background_color = Color::DarkCyan;
  113. buddy0.color = Color::White;
  114. buddy1.color = Color::White;
  115. m_editor->update();
  116. };
  117. struct MatchingTokenPair {
  118. CppToken::Type open;
  119. CppToken::Type close;
  120. };
  121. MatchingTokenPair pairs[] = {
  122. { CppToken::Type::LeftCurly, CppToken::Type::RightCurly },
  123. { CppToken::Type::LeftParen, CppToken::Type::RightParen },
  124. { CppToken::Type::LeftBracket, CppToken::Type::RightBracket },
  125. };
  126. for (size_t i = 0; i < document.spans().size(); ++i) {
  127. auto& span = const_cast<GUI::TextDocumentSpan&>(document.spans().at(i));
  128. auto token_type = (CppToken::Type)((FlatPtr)span.data);
  129. for (auto& pair : pairs) {
  130. if (token_type == pair.open && span.range.start() == m_editor->cursor()) {
  131. auto buddy = find_span_of_type(i, pair.close, pair.open, Direction::Forward);
  132. if (buddy.has_value())
  133. make_buddies(i, buddy.value());
  134. return;
  135. }
  136. }
  137. auto right_of_end = span.range.end();
  138. right_of_end.set_column(right_of_end.column() + 1);
  139. for (auto& pair : pairs) {
  140. if (token_type == pair.close && right_of_end == m_editor->cursor()) {
  141. auto buddy = find_span_of_type(i, pair.open, pair.close, Direction::Backward);
  142. if (buddy.has_value())
  143. make_buddies(i, buddy.value());
  144. return;
  145. }
  146. }
  147. }
  148. }
  149. CppSyntaxHighlighter::~CppSyntaxHighlighter()
  150. {
  151. }
  152. }