SemanticSyntaxHighlighter.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. /*
  2. * Copyright (c) 2022, Itamar S. <itamar8910@gmail.com>
  3. * Copyright (c) 2022, the SerenityOS developers.
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include "SemanticSyntaxHighlighter.h"
  8. #include "Lexer.h"
  9. #include <LibDiff/Generator.h>
  10. #include <LibGUI/AutocompleteProvider.h>
  11. #include <LibGfx/Palette.h>
  12. namespace Cpp {
  13. void SemanticSyntaxHighlighter::rehighlight(Palette const& palette)
  14. {
  15. Vector<CodeComprehension::TokenInfo> new_tokens_info;
  16. auto text = m_client->get_text();
  17. {
  18. Threading::MutexLocker locker(m_lock);
  19. Cpp::Lexer lexer(text);
  20. lexer.set_ignore_whitespace(true);
  21. auto current_tokens = lexer.lex();
  22. // Identify folding regions
  23. Vector<Token> folding_region_start_tokens;
  24. Vector<GUI::TextDocumentFoldingRegion> folding_regions;
  25. for (auto& token : current_tokens) {
  26. if (token.type() == Token::Type::LeftCurly) {
  27. folding_region_start_tokens.append(token);
  28. } else if (token.type() == Token::Type::RightCurly) {
  29. if (!folding_region_start_tokens.is_empty()) {
  30. auto start_token = folding_region_start_tokens.take_last();
  31. GUI::TextDocumentFoldingRegion folding_region;
  32. folding_region.range.set_start({ start_token.end().line, start_token.end().column });
  33. folding_region.range.set_end({ token.start().line, token.start().column });
  34. folding_regions.append(move(folding_region));
  35. }
  36. }
  37. }
  38. m_client->do_set_folding_regions(move(folding_regions));
  39. StringBuilder current_tokens_as_lines;
  40. StringBuilder previous_tokens_as_lines;
  41. for (auto& token : current_tokens)
  42. current_tokens_as_lines.appendff("{}\n", token.type_as_byte_string());
  43. for (Cpp::Token const& token : m_saved_tokens)
  44. previous_tokens_as_lines.appendff("{}\n", token.type_as_byte_string());
  45. auto previous = previous_tokens_as_lines.to_byte_string();
  46. auto current = current_tokens_as_lines.to_byte_string();
  47. // FIXME: Computing the diff on the entire document's tokens is quite inefficient.
  48. // An improvement over this could be only including the tokens that are in edited text ranges in the diff.
  49. auto diff_hunks = Diff::from_text(previous.view(), current.view()).release_value_but_fixme_should_propagate_errors();
  50. for (auto& token : current_tokens) {
  51. new_tokens_info.append(CodeComprehension::TokenInfo { CodeComprehension::TokenInfo::SemanticType::Unknown,
  52. token.start().line, token.start().column, token.end().line, token.end().column });
  53. }
  54. size_t previous_token_index = 0;
  55. size_t current_token_index = 0;
  56. for (auto const& hunk : diff_hunks) {
  57. for (; previous_token_index < hunk.location.old_range.start_line; ++previous_token_index) {
  58. new_tokens_info[current_token_index].type = m_tokens_info[previous_token_index].type;
  59. ++current_token_index;
  60. }
  61. for (size_t i = 0; i < hunk.location.new_range.number_of_lines; ++i) {
  62. ++current_token_index;
  63. }
  64. for (size_t i = 0; i < hunk.location.old_range.number_of_lines; ++i) {
  65. ++previous_token_index;
  66. }
  67. }
  68. while (current_token_index < new_tokens_info.size() && previous_token_index < m_tokens_info.size()) {
  69. new_tokens_info[current_token_index].type = m_tokens_info[previous_token_index].type;
  70. ++previous_token_index;
  71. ++current_token_index;
  72. }
  73. }
  74. update_spans(new_tokens_info, palette);
  75. }
  76. static Gfx::TextAttributes style_for_token_type(Gfx::Palette const& palette, CodeComprehension::TokenInfo::SemanticType type)
  77. {
  78. switch (type) {
  79. case CodeComprehension::TokenInfo::SemanticType::Unknown:
  80. return { palette.base_text() };
  81. case CodeComprehension::TokenInfo::SemanticType::Keyword:
  82. return { palette.syntax_keyword(), {}, true };
  83. case CodeComprehension::TokenInfo::SemanticType::Type:
  84. return { palette.syntax_type(), {}, true };
  85. case CodeComprehension::TokenInfo::SemanticType::Identifier:
  86. return { palette.syntax_identifier() };
  87. case CodeComprehension::TokenInfo::SemanticType::String:
  88. return { palette.syntax_string() };
  89. case CodeComprehension::TokenInfo::SemanticType::Number:
  90. return { palette.syntax_number() };
  91. case CodeComprehension::TokenInfo::SemanticType::IncludePath:
  92. return { palette.syntax_preprocessor_value() };
  93. case CodeComprehension::TokenInfo::SemanticType::PreprocessorStatement:
  94. return { palette.syntax_preprocessor_statement() };
  95. case CodeComprehension::TokenInfo::SemanticType::Comment:
  96. return { palette.syntax_comment() };
  97. case CodeComprehension::TokenInfo::SemanticType::Function:
  98. return { palette.syntax_function() };
  99. case CodeComprehension::TokenInfo::SemanticType::Variable:
  100. return { palette.syntax_variable() };
  101. case CodeComprehension::TokenInfo::SemanticType::CustomType:
  102. return { palette.syntax_custom_type() };
  103. case CodeComprehension::TokenInfo::SemanticType::Namespace:
  104. return { palette.syntax_namespace() };
  105. case CodeComprehension::TokenInfo::SemanticType::Member:
  106. return { palette.syntax_member() };
  107. case CodeComprehension::TokenInfo::SemanticType::Parameter:
  108. return { palette.syntax_parameter() };
  109. case CodeComprehension::TokenInfo::SemanticType::PreprocessorMacro:
  110. return { palette.syntax_preprocessor_value() };
  111. default:
  112. VERIFY_NOT_REACHED();
  113. return { palette.base_text() };
  114. }
  115. }
  116. void SemanticSyntaxHighlighter::update_spans(Vector<CodeComprehension::TokenInfo> const& tokens_info, Gfx::Palette const& palette)
  117. {
  118. Vector<GUI::TextDocumentSpan> spans;
  119. for (auto& token : tokens_info) {
  120. // 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.
  121. GUI::TextDocumentSpan span;
  122. span.range.set_start({ token.start_line, token.start_column });
  123. span.range.set_end({ token.end_line, token.end_column + 1 });
  124. span.attributes = style_for_token_type(palette, token.type);
  125. span.is_skippable = token.type == CodeComprehension::TokenInfo::SemanticType::Whitespace;
  126. span.data = static_cast<u64>(token.type);
  127. spans.append(span);
  128. }
  129. m_client->do_set_spans(move(spans));
  130. m_has_brace_buddies = false;
  131. highlight_matching_token_pair();
  132. m_client->do_update();
  133. }
  134. void SemanticSyntaxHighlighter::update_tokens_info(Vector<CodeComprehension::TokenInfo> tokens_info)
  135. {
  136. {
  137. Threading::MutexLocker locker(m_lock);
  138. m_tokens_info = move(tokens_info);
  139. m_saved_tokens_text = m_client->get_text();
  140. Cpp::Lexer lexer(m_saved_tokens_text);
  141. lexer.set_ignore_whitespace(true);
  142. m_saved_tokens = lexer.lex();
  143. }
  144. }
  145. bool SemanticSyntaxHighlighter::is_identifier(u64 token_type) const
  146. {
  147. auto type = static_cast<CodeComprehension::TokenInfo::SemanticType>(token_type);
  148. return type == CodeComprehension::TokenInfo::SemanticType::Identifier
  149. || type == CodeComprehension::TokenInfo::SemanticType::Function
  150. || type == CodeComprehension::TokenInfo::SemanticType::Variable
  151. || type == CodeComprehension::TokenInfo::SemanticType::CustomType
  152. || type == CodeComprehension::TokenInfo::SemanticType::Namespace
  153. || type == CodeComprehension::TokenInfo::SemanticType::Member
  154. || type == CodeComprehension::TokenInfo::SemanticType::Parameter
  155. || type == CodeComprehension::TokenInfo::SemanticType::PreprocessorMacro;
  156. }
  157. bool SemanticSyntaxHighlighter::is_navigatable(u64 token_type) const
  158. {
  159. return static_cast<CodeComprehension::TokenInfo::SemanticType>(token_type) == CodeComprehension::TokenInfo::SemanticType::IncludePath;
  160. }
  161. }