ソースを参照

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.
Brian Gianforcaro 3 年 前
コミット
8e8d24fe29

+ 2 - 0
Userland/Libraries/LibGUI/CMakeLists.txt

@@ -42,6 +42,8 @@ set(SOURCES
     FontPicker.cpp
     FontPicker.cpp
     FontPickerDialogGML.h
     FontPickerDialogGML.h
     Frame.cpp
     Frame.cpp
+    GitCommitLexer.cpp
+    GitCommitSyntaxHighlighter.cpp
     GlyphMapWidget.cpp
     GlyphMapWidget.cpp
     GMLAutocompleteProvider.cpp
     GMLAutocompleteProvider.cpp
     GMLFormatter.cpp
     GMLFormatter.cpp

+ 82 - 0
Userland/Libraries/LibGUI/GitCommitLexer.cpp

@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2022, Brian Gianforcaro <bgianf@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/CharacterTypes.h>
+#include <AK/Vector.h>
+#include <LibGUI/GitCommitLexer.h>
+
+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<GitCommitToken> GitCommitLexer::lex()
+{
+    Vector<GitCommitToken> 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;
+}
+
+}

+ 62 - 0
Userland/Libraries/LibGUI/GitCommitLexer.h

@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2022, Brian Gianforcaro <bgianf@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/StringView.h>
+
+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<GitCommitToken> lex();
+
+private:
+    char peek(size_t offset = 0) const;
+    char consume();
+
+    StringView m_input;
+    size_t m_index { 0 };
+    GitCommitPosition m_position { 0, 0 };
+};
+
+}

+ 59 - 0
Userland/Libraries/LibGUI/GitCommitSyntaxHighlighter.cpp

@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2022, Brian Gianforcaro <bgianf@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibGUI/GitCommitLexer.h>
+#include <LibGUI/GitCommitSyntaxHighlighter.h>
+#include <LibGfx/Palette.h>
+
+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<GUI::TextDocumentSpan> 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<u64>(token.m_type);
+        spans.append(span);
+    }
+    m_client->do_set_spans(move(spans));
+    m_client->do_update();
+}
+
+Vector<GitCommitSyntaxHighlighter::MatchingTokenPair> GitCommitSyntaxHighlighter::matching_token_pairs_impl() const
+{
+    static Vector<MatchingTokenPair> empty;
+    return empty;
+}
+
+bool GitCommitSyntaxHighlighter::token_types_equal(u64 token1, u64 token2) const
+{
+    return static_cast<GUI::GitCommitToken::Type>(token1) == static_cast<GUI::GitCommitToken::Type>(token2);
+}
+
+GitCommitSyntaxHighlighter::~GitCommitSyntaxHighlighter()
+{
+}
+
+}

+ 26 - 0
Userland/Libraries/LibGUI/GitCommitSyntaxHighlighter.h

@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2022, Brian Gianforcaro <bgianf@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibSyntax/Highlighter.h>
+
+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<MatchingTokenPair> matching_token_pairs_impl() const override;
+    virtual bool token_types_equal(u64, u64) const override;
+};
+
+}