diff --git a/Userland/Libraries/LibCMake/CMakeLists.txt b/Userland/Libraries/LibCMake/CMakeLists.txt index 58f7df5f0a..89ac0181af 100644 --- a/Userland/Libraries/LibCMake/CMakeLists.txt +++ b/Userland/Libraries/LibCMake/CMakeLists.txt @@ -1,5 +1,6 @@ set(SOURCES Lexer.cpp + SyntaxHighlighter.cpp Token.cpp ) diff --git a/Userland/Libraries/LibCMake/SyntaxHighlighter.cpp b/Userland/Libraries/LibCMake/SyntaxHighlighter.cpp new file mode 100644 index 0000000000..d5f85cbb41 --- /dev/null +++ b/Userland/Libraries/LibCMake/SyntaxHighlighter.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2023, Sam Atkins + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "SyntaxHighlighter.h" +#include +#include + +namespace CMake { + +static Syntax::TextStyle style_for_token_type(Gfx::Palette const& palette, Token::Type type) +{ + switch (type) { + case Token::Type::BracketComment: + case Token::Type::LineComment: + return { palette.syntax_comment() }; + case Token::Type::Identifier: + return { palette.syntax_function() }; + case Token::Type::ControlKeyword: + return { palette.syntax_control_keyword() }; + case Token::Type::OpenParen: + case Token::Type::CloseParen: + return { palette.syntax_punctuation() }; + case Token::Type::BracketArgument: + return { palette.syntax_parameter() }; + case Token::Type::QuotedArgument: + return { palette.syntax_string() }; + case Token::Type::UnquotedArgument: + return { palette.syntax_parameter() }; + case Token::Type::Garbage: + return { palette.red() }; + case Token::Type::VariableReference: + // This is a bit arbitrary, since we don't have a color specifically for this. + return { palette.syntax_preprocessor_value() }; + default: + return { palette.base_text() }; + } +} + +bool SyntaxHighlighter::is_identifier(u64 token_type) const +{ + auto cmake_token = static_cast(token_type); + return cmake_token == Token::Type::Identifier; +} + +void SyntaxHighlighter::rehighlight(Gfx::Palette const& palette) +{ + auto text = m_client->get_text(); + auto tokens = Lexer::lex(text).release_value_but_fixme_should_propagate_errors(); + + Vector spans; + auto highlight_span = [&](Token::Type type, Position const& start, Position const& end) { + GUI::TextDocumentSpan span; + span.range.set_start({ start.line, start.column }); + span.range.set_end({ end.line, end.column }); + if (!span.range.is_valid()) + return; + + auto style = style_for_token_type(palette, type); + span.attributes.color = style.color; + span.attributes.bold = style.bold; + if (type == Token::Type::Garbage) { + span.attributes.underline = true; + span.attributes.underline_color = palette.red(); + span.attributes.underline_style = Gfx::TextAttributes::UnderlineStyle::Wavy; + } + span.is_skippable = false; + span.data = static_cast(type); + spans.append(move(span)); + }; + + for (auto const& token : tokens) { + if (token.type == Token::Type::QuotedArgument || token.type == Token::Type::UnquotedArgument) { + // Alternately highlight the regular/variable-reference parts. + // 0-length ranges are caught in highlight_span() so we don't have to worry about them. + Position previous_position = token.start; + for (auto const& reference : token.variable_references) { + highlight_span(token.type, previous_position, reference.start); + highlight_span(Token::Type::VariableReference, reference.start, reference.end); + previous_position = reference.end; + } + highlight_span(token.type, previous_position, token.end); + continue; + } + + highlight_span(token.type, token.start, token.end); + } + m_client->do_set_spans(move(spans)); + + m_has_brace_buddies = false; + highlight_matching_token_pair(); + + m_client->do_update(); +} + +Vector SyntaxHighlighter::matching_token_pairs_impl() const +{ + static Vector pairs; + if (pairs.is_empty()) { + pairs.append({ static_cast(Token::Type::OpenParen), static_cast(Token::Type::CloseParen) }); + } + return pairs; +} + +bool SyntaxHighlighter::token_types_equal(u64 token1, u64 token2) const +{ + return static_cast(token1) == static_cast(token2); +} + +} diff --git a/Userland/Libraries/LibCMake/SyntaxHighlighter.h b/Userland/Libraries/LibCMake/SyntaxHighlighter.h new file mode 100644 index 0000000000..74aa4fd17f --- /dev/null +++ b/Userland/Libraries/LibCMake/SyntaxHighlighter.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2023, Sam Atkins + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace CMake { + +class SyntaxHighlighter final : public Syntax::Highlighter { +public: + SyntaxHighlighter() = default; + virtual ~SyntaxHighlighter() override = default; + + virtual bool is_identifier(u64) const override; + + virtual Syntax::Language language() const override { return Syntax::Language::CMake; } + virtual Optional comment_prefix() const override { return "#"sv; } + virtual Optional comment_suffix() const override { return {}; } + virtual void rehighlight(Palette const&) override; + +protected: + virtual Vector matching_token_pairs_impl() const override; + virtual bool token_types_equal(u64, u64) const override; +}; + +} diff --git a/Userland/Libraries/LibSyntax/Highlighter.cpp b/Userland/Libraries/LibSyntax/Highlighter.cpp index e4b8189f10..93ee9b6aab 100644 --- a/Userland/Libraries/LibSyntax/Highlighter.cpp +++ b/Userland/Libraries/LibSyntax/Highlighter.cpp @@ -141,6 +141,8 @@ void Highlighter::register_nested_token_pairs(Vector pairs) StringView language_to_string(Language language) { switch (language) { + case Language::CMake: + return "CMake"sv; case Language::Cpp: return "C++"sv; case Language::CSS: @@ -168,6 +170,8 @@ StringView language_to_string(Language language) StringView common_language_extension(Language language) { switch (language) { + case Language::CMake: + return "cmake"sv; case Language::Cpp: return "cpp"sv; case Language::CSS: diff --git a/Userland/Libraries/LibSyntax/Highlighter.h b/Userland/Libraries/LibSyntax/Highlighter.h index 5ddb22c240..778bf129e7 100644 --- a/Userland/Libraries/LibSyntax/Highlighter.h +++ b/Userland/Libraries/LibSyntax/Highlighter.h @@ -15,6 +15,7 @@ namespace Syntax { enum class Language { + CMake, Cpp, CSS, GitCommit,