SyntaxHighlighter.cpp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. /*
  2. * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibWeb/HTML/Parser/HTMLTokenizer.h>
  7. #include <LibWeb/HTML/SyntaxHighlighter/SyntaxHighlighter.h>
  8. namespace Web::HTML {
  9. enum class AugmentedTokenKind : u32 {
  10. AttributeName,
  11. AttributeValue,
  12. OpenTag,
  13. CloseTag,
  14. Comment,
  15. Doctype,
  16. };
  17. bool SyntaxHighlighter::is_identifier(void* token) const
  18. {
  19. if (!token)
  20. return false;
  21. return false;
  22. }
  23. bool SyntaxHighlighter::is_navigatable(void*) const
  24. {
  25. return false;
  26. }
  27. void SyntaxHighlighter::rehighlight(const Palette& palette)
  28. {
  29. (void)palette;
  30. auto text = m_client->get_text();
  31. Vector<GUI::TextDocumentSpan> spans;
  32. auto highlight = [&](auto start_line, auto start_column, auto end_line, auto end_column, Gfx::TextAttributes attributes, AugmentedTokenKind kind) {
  33. spans.empend(
  34. GUI::TextRange {
  35. { start_line, start_column },
  36. { end_line, end_column },
  37. },
  38. move(attributes),
  39. (void*)kind,
  40. false);
  41. };
  42. HTMLTokenizer tokenizer { text, "utf-8" };
  43. [[maybe_unused]] enum class State {
  44. HTML,
  45. Javascript,
  46. CSS,
  47. } state { State::HTML };
  48. for (;;) {
  49. auto token = tokenizer.next_token();
  50. if (!token.has_value())
  51. break;
  52. if (token->is_start_tag()) {
  53. if (token->tag_name() == "script"sv) {
  54. tokenizer.switch_to(HTMLTokenizer::State::ScriptData);
  55. state = State::Javascript;
  56. } else if (token->tag_name() == "style"sv) {
  57. tokenizer.switch_to(HTMLTokenizer::State::RAWTEXT);
  58. state = State::CSS;
  59. }
  60. } else if (token->is_end_tag()) {
  61. if (token->tag_name().is_one_of("script"sv, "style"sv)) {
  62. if (state == State::Javascript) {
  63. // FIXME: Highlight javascript code here instead.
  64. } else if (state == State::CSS) {
  65. // FIXME: Highlight CSS code here instead.
  66. }
  67. state = State::HTML;
  68. }
  69. }
  70. size_t token_start_offset = token->is_end_tag() ? 1 : 0;
  71. if (token->is_comment()) {
  72. highlight(
  73. token->start_position().line,
  74. token->start_position().column,
  75. token->start_position().line,
  76. token->start_position().column,
  77. { palette.syntax_comment(), {} },
  78. AugmentedTokenKind::Comment);
  79. } else if (token->is_start_tag() || token->is_end_tag()) {
  80. // FIXME: This breaks with single-character tag names.
  81. highlight(
  82. token->start_position().line,
  83. token->start_position().column + token_start_offset,
  84. token->start_position().line,
  85. token->start_position().column + token->tag_name().length() + token_start_offset - 1,
  86. { palette.syntax_keyword(), {} },
  87. token->is_start_tag() ? AugmentedTokenKind::OpenTag : AugmentedTokenKind::CloseTag);
  88. for (auto& attribute : token->attributes()) {
  89. highlight(
  90. attribute.name_start_position.line,
  91. attribute.name_start_position.column + token_start_offset,
  92. attribute.name_end_position.line,
  93. attribute.name_end_position.column + token_start_offset,
  94. { palette.syntax_identifier(), {} },
  95. AugmentedTokenKind::AttributeName);
  96. highlight(
  97. attribute.value_start_position.line,
  98. attribute.value_start_position.column + token_start_offset,
  99. attribute.value_end_position.line,
  100. attribute.value_end_position.column + token_start_offset,
  101. { palette.syntax_string(), {} },
  102. AugmentedTokenKind::AttributeValue);
  103. }
  104. } else if (token->is_doctype()) {
  105. highlight(
  106. token->start_position().line,
  107. token->start_position().column,
  108. token->start_position().line,
  109. token->start_position().column,
  110. { palette.syntax_preprocessor_statement(), {} },
  111. AugmentedTokenKind::Doctype);
  112. }
  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. Vector<Syntax::Highlighter::MatchingTokenPair> SyntaxHighlighter::matching_token_pairs() const
  120. {
  121. static Vector<MatchingTokenPair> pairs;
  122. if (pairs.is_empty()) {
  123. pairs.append({ (void*)AugmentedTokenKind::OpenTag, (void*)AugmentedTokenKind::CloseTag });
  124. }
  125. return pairs;
  126. }
  127. bool SyntaxHighlighter::token_types_equal(void* token0, void* token1) const
  128. {
  129. return token0 == token1;
  130. }
  131. }