INISyntaxHighlighter.cpp 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. /*
  2. * Copyright (c) 2020, Hüseyin Aslıtürk <asliturk@hotmail.com>
  3. * Copyright (c) 2022, the SerenityOS developers.
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <LibGUI/INILexer.h>
  8. #include <LibGUI/INISyntaxHighlighter.h>
  9. #include <LibGfx/Palette.h>
  10. namespace GUI {
  11. static Gfx::TextAttributes style_for_token_type(Gfx::Palette const& palette, IniToken::Type type)
  12. {
  13. switch (type) {
  14. case IniToken::Type::LeftBracket:
  15. case IniToken::Type::RightBracket:
  16. case IniToken::Type::Section:
  17. return { palette.syntax_keyword(), {}, true };
  18. case IniToken::Type::Name:
  19. return { palette.syntax_identifier() };
  20. case IniToken::Type::Value:
  21. return { palette.syntax_string() };
  22. case IniToken::Type::Comment:
  23. return { palette.syntax_comment() };
  24. case IniToken::Type::Equal:
  25. return { palette.syntax_operator(), {}, true };
  26. default:
  27. return { palette.base_text() };
  28. }
  29. }
  30. bool IniSyntaxHighlighter::is_identifier(u64 token) const
  31. {
  32. auto ini_token = static_cast<GUI::IniToken::Type>(token);
  33. return ini_token == GUI::IniToken::Type::Name;
  34. }
  35. void IniSyntaxHighlighter::rehighlight(Palette const& palette)
  36. {
  37. auto text = m_client->get_text();
  38. IniLexer lexer(text);
  39. auto tokens = lexer.lex();
  40. Optional<IniToken> previous_section_token;
  41. IniToken previous_token;
  42. Vector<TextDocumentFoldingRegion> folding_regions;
  43. Vector<GUI::TextDocumentSpan> spans;
  44. for (auto& token : tokens) {
  45. GUI::TextDocumentSpan span;
  46. span.range.set_start({ token.m_start.line, token.m_start.column });
  47. span.range.set_end({ token.m_end.line, token.m_end.column });
  48. span.attributes = style_for_token_type(palette, token.m_type);
  49. span.is_skippable = token.m_type == IniToken::Type::Whitespace;
  50. span.data = static_cast<u64>(token.m_type);
  51. spans.append(span);
  52. if (token.m_type == IniToken::Type::RightBracket && previous_token.m_type == IniToken::Type::Section) {
  53. previous_section_token = token;
  54. } else if (token.m_type == IniToken::Type::LeftBracket) {
  55. if (previous_section_token.has_value()) {
  56. TextDocumentFoldingRegion region;
  57. region.range.set_start({ previous_section_token->m_end.line, previous_section_token->m_end.column });
  58. // If possible, leave a blank line between sections.
  59. // `end_line - start_line > 1` means the whitespace contains at least 1 blank line,
  60. // so we can end the region 1 line before the end of that whitespace token.
  61. // (Otherwise, we'd end the region at the start of the line that begins the next section.)
  62. auto end_line = token.m_start.line;
  63. if (previous_token.m_type == IniToken::Type::Whitespace
  64. && (previous_token.m_end.line - previous_token.m_start.line > 1))
  65. end_line--;
  66. region.range.set_end({ end_line, token.m_start.column });
  67. folding_regions.append(move(region));
  68. }
  69. previous_section_token = token;
  70. }
  71. previous_token = token;
  72. }
  73. if (previous_section_token.has_value()) {
  74. TextDocumentFoldingRegion region;
  75. auto& end_token = tokens.last();
  76. region.range.set_start({ previous_section_token->m_end.line, previous_section_token->m_end.column });
  77. region.range.set_end({ end_token.m_end.line, end_token.m_end.column });
  78. folding_regions.append(move(region));
  79. }
  80. m_client->do_set_spans(move(spans));
  81. m_client->do_set_folding_regions(move(folding_regions));
  82. m_has_brace_buddies = false;
  83. highlight_matching_token_pair();
  84. m_client->do_update();
  85. }
  86. Vector<IniSyntaxHighlighter::MatchingTokenPair> IniSyntaxHighlighter::matching_token_pairs_impl() const
  87. {
  88. static Vector<MatchingTokenPair> pairs;
  89. if (pairs.is_empty()) {
  90. pairs.append({ static_cast<u64>(IniToken::Type::LeftBracket), static_cast<u64>(IniToken::Type::RightBracket) });
  91. }
  92. return pairs;
  93. }
  94. bool IniSyntaxHighlighter::token_types_equal(u64 token1, u64 token2) const
  95. {
  96. return static_cast<GUI::IniToken::Type>(token1) == static_cast<GUI::IniToken::Type>(token2);
  97. }
  98. }