diff options
Diffstat (limited to 'Userland/Libraries/LibSyntax')
-rw-r--r-- | Userland/Libraries/LibSyntax/CMakeLists.txt | 6 | ||||
-rw-r--r-- | Userland/Libraries/LibSyntax/Forward.h | 33 | ||||
-rw-r--r-- | Userland/Libraries/LibSyntax/Highlighter.cpp | 155 | ||||
-rw-r--r-- | Userland/Libraries/LibSyntax/Highlighter.h | 90 |
4 files changed, 284 insertions, 0 deletions
diff --git a/Userland/Libraries/LibSyntax/CMakeLists.txt b/Userland/Libraries/LibSyntax/CMakeLists.txt new file mode 100644 index 0000000000..0da883f497 --- /dev/null +++ b/Userland/Libraries/LibSyntax/CMakeLists.txt @@ -0,0 +1,6 @@ +set(SOURCES + Highlighter.cpp +) + +serenity_lib(LibSyntax syntax) +target_link_libraries(LibSyntax LibC) diff --git a/Userland/Libraries/LibSyntax/Forward.h b/Userland/Libraries/LibSyntax/Forward.h new file mode 100644 index 0000000000..55c62aa3cb --- /dev/null +++ b/Userland/Libraries/LibSyntax/Forward.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021, Andreas Kling <kling@serenityos.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +namespace Syntax { + +class Highlighter; + +} diff --git a/Userland/Libraries/LibSyntax/Highlighter.cpp b/Userland/Libraries/LibSyntax/Highlighter.cpp new file mode 100644 index 0000000000..53c13909a2 --- /dev/null +++ b/Userland/Libraries/LibSyntax/Highlighter.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2020-2021, the SerenityOS developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <LibGUI/TextEditor.h> +#include <LibSyntax/Highlighter.h> + +namespace Syntax { + +Highlighter::~Highlighter() +{ +} + +void Highlighter::highlight_matching_token_pair() +{ + ASSERT(m_editor); + auto& document = m_editor->document(); + + enum class Direction { + Forward, + Backward, + }; + + auto find_span_of_type = [&](auto i, void* type, void* not_type, Direction direction) -> Optional<size_t> { + size_t nesting_level = 0; + bool forward = direction == Direction::Forward; + + if (forward) { + ++i; + if (i >= document.spans().size()) + return {}; + } else { + if (i == 0) + return {}; + --i; + } + + for (;;) { + auto& span = document.spans().at(i); + auto span_token_type = span.data; + if (token_types_equal(span_token_type, not_type)) { + ++nesting_level; + } else if (token_types_equal(span_token_type, type)) { + if (nesting_level-- <= 0) + return i; + } + + if (forward) { + ++i; + if (i >= document.spans().size()) + return {}; + } else { + if (i == 0) + return {}; + --i; + } + } + + return {}; + }; + + auto make_buddies = [&](int index0, int index1) { + auto& buddy0 = document.spans()[index0]; + auto& buddy1 = document.spans()[index1]; + m_has_brace_buddies = true; + m_brace_buddies[0].index = index0; + m_brace_buddies[1].index = index1; + m_brace_buddies[0].span_backup = buddy0; + m_brace_buddies[1].span_backup = buddy1; + buddy0.attributes.background_color = Color::DarkCyan; + buddy1.attributes.background_color = Color::DarkCyan; + buddy0.attributes.color = Color::White; + buddy1.attributes.color = Color::White; + m_editor->update(); + }; + + auto pairs = matching_token_pairs(); + + for (size_t i = 0; i < document.spans().size(); ++i) { + auto& span = const_cast<GUI::TextDocumentSpan&>(document.spans().at(i)); + auto token_type = span.data; + + for (auto& pair : pairs) { + if (token_types_equal(token_type, pair.open) && span.range.start() == m_editor->cursor()) { + auto buddy = find_span_of_type(i, pair.close, pair.open, Direction::Forward); + if (buddy.has_value()) + make_buddies(i, buddy.value()); + return; + } + } + + auto right_of_end = span.range.end(); + right_of_end.set_column(right_of_end.column() + 1); + + for (auto& pair : pairs) { + if (token_types_equal(token_type, pair.close) && right_of_end == m_editor->cursor()) { + auto buddy = find_span_of_type(i, pair.open, pair.close, Direction::Backward); + if (buddy.has_value()) + make_buddies(i, buddy.value()); + return; + } + } + } +} + +void Highlighter::attach(GUI::TextEditor& editor) +{ + ASSERT(!m_editor); + m_editor = editor; +} + +void Highlighter::detach() +{ + ASSERT(m_editor); + m_editor = nullptr; +} + +void Highlighter::cursor_did_change() +{ + ASSERT(m_editor); + auto& document = m_editor->document(); + if (m_has_brace_buddies) { + if (m_brace_buddies[0].index >= 0 && m_brace_buddies[0].index < static_cast<int>(document.spans().size())) + document.set_span_at_index(m_brace_buddies[0].index, m_brace_buddies[0].span_backup); + if (m_brace_buddies[1].index >= 0 && m_brace_buddies[1].index < static_cast<int>(document.spans().size())) + document.set_span_at_index(m_brace_buddies[1].index, m_brace_buddies[1].span_backup); + m_has_brace_buddies = false; + m_editor->update(); + } + highlight_matching_token_pair(); +} + +} diff --git a/Userland/Libraries/LibSyntax/Highlighter.h b/Userland/Libraries/LibSyntax/Highlighter.h new file mode 100644 index 0000000000..8ac7b2691c --- /dev/null +++ b/Userland/Libraries/LibSyntax/Highlighter.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2020-2021, the SerenityOS developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <AK/Noncopyable.h> +#include <AK/WeakPtr.h> +#include <LibGUI/TextDocument.h> +#include <LibGfx/Palette.h> + +namespace Syntax { + +enum class Language { + PlainText, + Cpp, + JavaScript, + INI, + GML, + Shell, +}; + +struct TextStyle { + const Gfx::Color color; + const bool bold { false }; +}; + +class Highlighter { + AK_MAKE_NONCOPYABLE(Highlighter); + AK_MAKE_NONMOVABLE(Highlighter); + +public: + virtual ~Highlighter(); + + virtual Language language() const = 0; + virtual void rehighlight(Gfx::Palette) = 0; + virtual void highlight_matching_token_pair(); + + virtual bool is_identifier(void*) const { return false; }; + virtual bool is_navigatable(void*) const { return false; }; + + void attach(GUI::TextEditor& editor); + void detach(); + void cursor_did_change(); + +protected: + Highlighter() { } + + WeakPtr<GUI::TextEditor> m_editor; + + struct MatchingTokenPair { + void* open; + void* close; + }; + + virtual Vector<MatchingTokenPair> matching_token_pairs() const = 0; + virtual bool token_types_equal(void*, void*) const = 0; + + struct BuddySpan { + int index { -1 }; + GUI::TextDocumentSpan span_backup; + }; + + bool m_has_brace_buddies { false }; + BuddySpan m_brace_buddies[2]; +}; + +} |