SemanticSyntaxHighlighter.cpp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  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. StringBuilder current_tokens_as_lines;
  23. StringBuilder previous_tokens_as_lines;
  24. for (auto& token : current_tokens)
  25. current_tokens_as_lines.appendff("{}\n", token.type_as_string());
  26. for (Cpp::Token const& token : m_saved_tokens)
  27. previous_tokens_as_lines.appendff("{}\n", token.type_as_string());
  28. auto previous = previous_tokens_as_lines.build();
  29. auto current = current_tokens_as_lines.build();
  30. // FIXME: Computing the diff on the entire document's tokens is quite inefficient.
  31. // An improvement over this could be only including the tokens that are in edited text ranges in the diff.
  32. auto diff_hunks = Diff::from_text(previous.view(), current.view());
  33. for (auto& token : current_tokens) {
  34. new_tokens_info.append(CodeComprehension::TokenInfo { CodeComprehension::TokenInfo::SemanticType::Unknown,
  35. token.start().line, token.start().column, token.end().line, token.end().column });
  36. }
  37. size_t previous_token_index = 0;
  38. size_t current_token_index = 0;
  39. for (auto const& hunk : diff_hunks) {
  40. for (; previous_token_index < hunk.original_start_line; ++previous_token_index) {
  41. new_tokens_info[current_token_index].type = m_tokens_info[previous_token_index].type;
  42. ++current_token_index;
  43. }
  44. for (size_t i = 0; i < hunk.added_lines.size(); ++i) {
  45. ++current_token_index;
  46. }
  47. for (size_t i = 0; i < hunk.removed_lines.size(); ++i) {
  48. ++previous_token_index;
  49. }
  50. }
  51. while (current_token_index < new_tokens_info.size() && previous_token_index < m_tokens_info.size()) {
  52. new_tokens_info[current_token_index].type = m_tokens_info[previous_token_index].type;
  53. ++previous_token_index;
  54. ++current_token_index;
  55. }
  56. }
  57. update_spans(new_tokens_info, palette);
  58. }
  59. static Syntax::TextStyle style_for_token_type(Gfx::Palette const& palette, CodeComprehension::TokenInfo::SemanticType type)
  60. {
  61. switch (type) {
  62. case CodeComprehension::TokenInfo::SemanticType::Unknown:
  63. return { palette.base_text(), false };
  64. case CodeComprehension::TokenInfo::SemanticType::Keyword:
  65. return { palette.syntax_keyword(), true };
  66. case CodeComprehension::TokenInfo::SemanticType::Type:
  67. return { palette.syntax_type(), true };
  68. case CodeComprehension::TokenInfo::SemanticType::Identifier:
  69. return { palette.syntax_identifier(), false };
  70. case CodeComprehension::TokenInfo::SemanticType::String:
  71. return { palette.syntax_string(), false };
  72. case CodeComprehension::TokenInfo::SemanticType::Number:
  73. return { palette.syntax_number(), false };
  74. case CodeComprehension::TokenInfo::SemanticType::IncludePath:
  75. return { palette.syntax_preprocessor_value(), false };
  76. case CodeComprehension::TokenInfo::SemanticType::PreprocessorStatement:
  77. return { palette.syntax_preprocessor_statement(), false };
  78. case CodeComprehension::TokenInfo::SemanticType::Comment:
  79. return { palette.syntax_comment(), false };
  80. case CodeComprehension::TokenInfo::SemanticType::Function:
  81. return { palette.syntax_function(), false };
  82. case CodeComprehension::TokenInfo::SemanticType::Variable:
  83. return { palette.syntax_variable(), false };
  84. case CodeComprehension::TokenInfo::SemanticType::CustomType:
  85. return { palette.syntax_custom_type(), false };
  86. case CodeComprehension::TokenInfo::SemanticType::Namespace:
  87. return { palette.syntax_namespace(), false };
  88. case CodeComprehension::TokenInfo::SemanticType::Member:
  89. return { palette.syntax_member(), false };
  90. case CodeComprehension::TokenInfo::SemanticType::Parameter:
  91. return { palette.syntax_parameter(), false };
  92. case CodeComprehension::TokenInfo::SemanticType::PreprocessorMacro:
  93. return { palette.syntax_preprocessor_value(), false };
  94. default:
  95. VERIFY_NOT_REACHED();
  96. return { palette.base_text(), false };
  97. }
  98. }
  99. void SemanticSyntaxHighlighter::update_spans(Vector<CodeComprehension::TokenInfo> const& tokens_info, Gfx::Palette const& pallete)
  100. {
  101. Vector<GUI::TextDocumentSpan> spans;
  102. for (auto& token : tokens_info) {
  103. // 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.
  104. GUI::TextDocumentSpan span;
  105. span.range.set_start({ token.start_line, token.start_column });
  106. span.range.set_end({ token.end_line, token.end_column + 1 });
  107. auto style = style_for_token_type(pallete, token.type);
  108. span.attributes.color = style.color;
  109. span.attributes.bold = style.bold;
  110. span.is_skippable = token.type == CodeComprehension::TokenInfo::SemanticType::Whitespace;
  111. span.data = static_cast<u64>(token.type);
  112. spans.append(span);
  113. }
  114. m_client->do_set_spans(move(spans));
  115. m_has_brace_buddies = false;
  116. highlight_matching_token_pair();
  117. m_client->do_update();
  118. }
  119. void SemanticSyntaxHighlighter::update_tokens_info(Vector<CodeComprehension::TokenInfo> tokens_info)
  120. {
  121. {
  122. Threading::MutexLocker locker(m_lock);
  123. m_tokens_info = move(tokens_info);
  124. m_saved_tokens_text = m_client->get_text();
  125. Cpp::Lexer lexer(m_saved_tokens_text);
  126. lexer.set_ignore_whitespace(true);
  127. m_saved_tokens = lexer.lex();
  128. }
  129. }
  130. bool SemanticSyntaxHighlighter::is_identifier(u64 token_type) const
  131. {
  132. auto type = static_cast<CodeComprehension::TokenInfo::SemanticType>(token_type);
  133. return type == CodeComprehension::TokenInfo::SemanticType::Identifier
  134. || type == CodeComprehension::TokenInfo::SemanticType::Function
  135. || type == CodeComprehension::TokenInfo::SemanticType::Variable
  136. || type == CodeComprehension::TokenInfo::SemanticType::CustomType
  137. || type == CodeComprehension::TokenInfo::SemanticType::Namespace
  138. || type == CodeComprehension::TokenInfo::SemanticType::Member
  139. || type == CodeComprehension::TokenInfo::SemanticType::Parameter
  140. || type == CodeComprehension::TokenInfo::SemanticType::PreprocessorMacro;
  141. }
  142. bool SemanticSyntaxHighlighter::is_navigatable(u64 token_type) const
  143. {
  144. return static_cast<CodeComprehension::TokenInfo::SemanticType>(token_type) == CodeComprehension::TokenInfo::SemanticType::IncludePath;
  145. }
  146. }