From 8e8d24fe29ac5070504e911821e382644ebfa5c9 Mon Sep 17 00:00:00 2001 From: Brian Gianforcaro Date: Mon, 17 Jan 2022 19:36:09 -0800 Subject: [PATCH] LibGUI: Add a GitCommit SyntaxHighlighter implementation This highlighter just syntax highlights the commented lines in your git commit message. It could potentially be enhanced to handle the rebase UI or other more advanced cases in the future. --- Userland/Libraries/LibGUI/CMakeLists.txt | 2 + Userland/Libraries/LibGUI/GitCommitLexer.cpp | 82 +++++++++++++++++++ Userland/Libraries/LibGUI/GitCommitLexer.h | 62 ++++++++++++++ .../LibGUI/GitCommitSyntaxHighlighter.cpp | 59 +++++++++++++ .../LibGUI/GitCommitSyntaxHighlighter.h | 26 ++++++ 5 files changed, 231 insertions(+) create mode 100644 Userland/Libraries/LibGUI/GitCommitLexer.cpp create mode 100644 Userland/Libraries/LibGUI/GitCommitLexer.h create mode 100644 Userland/Libraries/LibGUI/GitCommitSyntaxHighlighter.cpp create mode 100644 Userland/Libraries/LibGUI/GitCommitSyntaxHighlighter.h diff --git a/Userland/Libraries/LibGUI/CMakeLists.txt b/Userland/Libraries/LibGUI/CMakeLists.txt index 39cdc7c448..44c1d6c498 100644 --- a/Userland/Libraries/LibGUI/CMakeLists.txt +++ b/Userland/Libraries/LibGUI/CMakeLists.txt @@ -42,6 +42,8 @@ set(SOURCES FontPicker.cpp FontPickerDialogGML.h Frame.cpp + GitCommitLexer.cpp + GitCommitSyntaxHighlighter.cpp GlyphMapWidget.cpp GMLAutocompleteProvider.cpp GMLFormatter.cpp diff --git a/Userland/Libraries/LibGUI/GitCommitLexer.cpp b/Userland/Libraries/LibGUI/GitCommitLexer.cpp new file mode 100644 index 0000000000..43a14af763 --- /dev/null +++ b/Userland/Libraries/LibGUI/GitCommitLexer.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2022, Brian Gianforcaro + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +namespace GUI { + +GitCommitLexer::GitCommitLexer(StringView input) + : m_input(input) +{ +} + +char GitCommitLexer::peek(size_t offset) const +{ + if ((m_index + offset) >= m_input.length()) + return 0; + return m_input[m_index + offset]; +} + +char GitCommitLexer::consume() +{ + VERIFY(m_index < m_input.length()); + char ch = m_input[m_index++]; + if (ch == '\n') { + m_position.line++; + m_position.column = 0; + } else { + m_position.column++; + } + return ch; +} + +Vector GitCommitLexer::lex() +{ + Vector tokens; + + size_t token_start_index = 0; + GitCommitPosition token_start_position; + + auto begin_token = [&] { + token_start_index = m_index; + token_start_position = m_position; + }; + + auto commit_token = [&](auto type) { + GitCommitToken token; + token.m_view = m_input.substring_view(token_start_index, m_index - token_start_index); + token.m_type = type; + token.m_start = token_start_position; + token.m_end = m_position; + tokens.append(token); + }; + + while (m_index < m_input.length()) { + if (is_ascii_space(peek(0))) { + begin_token(); + while (is_ascii_space(peek())) + consume(); + continue; + } + + // Commit comments + if (peek(0) && peek(0) == '#') { + begin_token(); + while (peek() && peek() != '\n') + consume(); + commit_token(GitCommitToken::Type::Comment); + continue; + } + + consume(); + commit_token(GitCommitToken::Type::Unknown); + } + return tokens; +} + +} diff --git a/Userland/Libraries/LibGUI/GitCommitLexer.h b/Userland/Libraries/LibGUI/GitCommitLexer.h new file mode 100644 index 0000000000..52dd40badb --- /dev/null +++ b/Userland/Libraries/LibGUI/GitCommitLexer.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2022, Brian Gianforcaro + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace GUI { + +#define FOR_EACH_TOKEN_TYPE \ + __TOKEN(Comment) \ + __TOKEN(Unknown) + +struct GitCommitPosition { + size_t line; + size_t column; +}; + +struct GitCommitToken { + enum class Type { +#define __TOKEN(x) x, + FOR_EACH_TOKEN_TYPE +#undef __TOKEN + }; + + char const* to_string() const + { + switch (m_type) { +#define __TOKEN(x) \ + case Type::x: \ + return #x; + FOR_EACH_TOKEN_TYPE +#undef __TOKEN + } + VERIFY_NOT_REACHED(); + } + + Type m_type { Type::Unknown }; + StringView m_view; + GitCommitPosition m_start; + GitCommitPosition m_end; +}; + +class GitCommitLexer { +public: + GitCommitLexer(StringView); + + Vector lex(); + +private: + char peek(size_t offset = 0) const; + char consume(); + + StringView m_input; + size_t m_index { 0 }; + GitCommitPosition m_position { 0, 0 }; +}; + +} diff --git a/Userland/Libraries/LibGUI/GitCommitSyntaxHighlighter.cpp b/Userland/Libraries/LibGUI/GitCommitSyntaxHighlighter.cpp new file mode 100644 index 0000000000..dbceb07550 --- /dev/null +++ b/Userland/Libraries/LibGUI/GitCommitSyntaxHighlighter.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2022, Brian Gianforcaro + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +namespace GUI { +static Syntax::TextStyle style_for_token_type(const Gfx::Palette& palette, GitCommitToken::Type type) +{ + switch (type) { + case GitCommitToken::Type::Comment: + return { palette.syntax_comment() }; + default: + return { palette.base_text() }; + } +} + +void GitCommitSyntaxHighlighter::rehighlight(Palette const& palette) +{ + auto text = m_client->get_text(); + GitCommitLexer lexer(text); + auto tokens = lexer.lex(); + + Vector spans; + for (auto& token : tokens) { + GUI::TextDocumentSpan span; + span.range.set_start({ token.m_start.line, token.m_start.column }); + span.range.set_end({ token.m_end.line, token.m_end.column }); + auto style = style_for_token_type(palette, token.m_type); + span.attributes.color = style.color; + span.attributes.bold = style.bold; + span.is_skippable = false; + span.data = static_cast(token.m_type); + spans.append(span); + } + m_client->do_set_spans(move(spans)); + m_client->do_update(); +} + +Vector GitCommitSyntaxHighlighter::matching_token_pairs_impl() const +{ + static Vector empty; + return empty; +} + +bool GitCommitSyntaxHighlighter::token_types_equal(u64 token1, u64 token2) const +{ + return static_cast(token1) == static_cast(token2); +} + +GitCommitSyntaxHighlighter::~GitCommitSyntaxHighlighter() +{ +} + +} diff --git a/Userland/Libraries/LibGUI/GitCommitSyntaxHighlighter.h b/Userland/Libraries/LibGUI/GitCommitSyntaxHighlighter.h new file mode 100644 index 0000000000..3e9cec60a7 --- /dev/null +++ b/Userland/Libraries/LibGUI/GitCommitSyntaxHighlighter.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2022, Brian Gianforcaro + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace GUI { + +class GitCommitSyntaxHighlighter final : public Syntax::Highlighter { +public: + GitCommitSyntaxHighlighter() { } + virtual ~GitCommitSyntaxHighlighter() override; + + virtual Syntax::Language language() const override { return Syntax::Language::GitCommit; } + virtual void rehighlight(Palette const&) override; + +protected: + virtual Vector matching_token_pairs_impl() const override; + virtual bool token_types_equal(u64, u64) const override; +}; + +}