CppSyntaxHighlighter.cpp 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  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::Number:
  22. return { Color::from_rgb(0x800000) };
  23. case CppToken::Type::EscapeSequence:
  24. return { Color::from_rgb(0x800080), &Gfx::Font::default_bold_fixed_width_font() };
  25. case CppToken::Type::PreprocessorStatement:
  26. return { Color::from_rgb(0x008080) };
  27. case CppToken::Type::Comment:
  28. return { Color::from_rgb(0x008000) };
  29. default:
  30. return { Color::Black };
  31. }
  32. }
  33. void CppSyntaxHighlighter::rehighlight()
  34. {
  35. ASSERT(m_editor);
  36. auto text = m_editor->text();
  37. CppLexer lexer(text);
  38. auto tokens = lexer.lex();
  39. Vector<GUI::TextDocumentSpan> spans;
  40. for (auto& token : tokens) {
  41. #ifdef DEBUG_SYNTAX_HIGHLIGHTING
  42. dbg() << token.to_string() << " @ " << token.m_start.line << ":" << token.m_start.column << " - " << token.m_end.line << ":" << token.m_end.column;
  43. #endif
  44. GUI::TextDocumentSpan span;
  45. span.range.set_start({ token.m_start.line, token.m_start.column });
  46. span.range.set_end({ token.m_end.line, token.m_end.column });
  47. auto style = style_for_token_type(token.m_type);
  48. span.color = style.color;
  49. span.font = style.font;
  50. span.is_skippable = token.m_type == CppToken::Type::Whitespace;
  51. span.data = (void*)token.m_type;
  52. spans.append(span);
  53. }
  54. m_editor->document().set_spans(spans);
  55. m_has_brace_buddies = false;
  56. highlight_matching_token_pair();
  57. m_editor->update();
  58. }
  59. void CppSyntaxHighlighter::highlight_matching_token_pair()
  60. {
  61. ASSERT(m_editor);
  62. auto& document = m_editor->document();
  63. enum class Direction {
  64. Forward,
  65. Backward,
  66. };
  67. auto find_span_of_type = [&](auto i, CppToken::Type type, CppToken::Type not_type, Direction direction) -> Optional<size_t> {
  68. size_t nesting_level = 0;
  69. bool forward = direction == Direction::Forward;
  70. for (forward ? ++i : --i; forward ? (i < document.spans().size()) : (i >= 0); forward ? ++i : --i) {
  71. auto& span = document.spans().at(i);
  72. auto span_token_type = (CppToken::Type)((FlatPtr)span.data);
  73. if (span_token_type == not_type) {
  74. ++nesting_level;
  75. } else if (span_token_type == type) {
  76. if (nesting_level-- <= 0)
  77. return i;
  78. }
  79. }
  80. return {};
  81. };
  82. auto make_buddies = [&](int index0, int index1) {
  83. auto& buddy0 = const_cast<GUI::TextDocumentSpan&>(document.spans()[index0]);
  84. auto& buddy1 = const_cast<GUI::TextDocumentSpan&>(document.spans()[index1]);
  85. m_has_brace_buddies = true;
  86. m_brace_buddies[0].index = index0;
  87. m_brace_buddies[1].index = index1;
  88. m_brace_buddies[0].span_backup = buddy0;
  89. m_brace_buddies[1].span_backup = buddy1;
  90. buddy0.background_color = Color::DarkCyan;
  91. buddy1.background_color = Color::DarkCyan;
  92. buddy0.color = Color::White;
  93. buddy1.color = Color::White;
  94. m_editor->update();
  95. };
  96. struct MatchingTokenPair {
  97. CppToken::Type open;
  98. CppToken::Type close;
  99. };
  100. MatchingTokenPair pairs[] = {
  101. { CppToken::Type::LeftCurly, CppToken::Type::RightCurly },
  102. { CppToken::Type::LeftParen, CppToken::Type::RightParen },
  103. { CppToken::Type::LeftBracket, CppToken::Type::RightBracket },
  104. };
  105. for (size_t i = 0; i < document.spans().size(); ++i) {
  106. auto& span = const_cast<GUI::TextDocumentSpan&>(document.spans().at(i));
  107. auto token_type = (CppToken::Type)((FlatPtr)span.data);
  108. for (auto& pair : pairs) {
  109. if (token_type == pair.open && span.range.start() == m_editor->cursor()) {
  110. auto buddy = find_span_of_type(i, pair.close, pair.open, Direction::Forward);
  111. if (buddy.has_value())
  112. make_buddies(i, buddy.value());
  113. return;
  114. }
  115. }
  116. auto right_of_end = span.range.end();
  117. right_of_end.set_column(right_of_end.column() + 1);
  118. for (auto& pair : pairs) {
  119. if (token_type == pair.close && right_of_end == m_editor->cursor()) {
  120. auto buddy = find_span_of_type(i, pair.open, pair.close, Direction::Backward);
  121. if (buddy.has_value())
  122. make_buddies(i, buddy.value());
  123. return;
  124. }
  125. }
  126. }
  127. }
  128. CppSyntaxHighlighter::~CppSyntaxHighlighter()
  129. {
  130. }
  131. }