From 5c022ac939257c125a0998ae2124df9ee54f8d12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=BCseyin=20ASLIT=C3=9CRK?= Date: Fri, 1 May 2020 02:28:58 +0300 Subject: LibGUI: INI file syntax highlighter --- Libraries/LibGUI/INILexer.cpp | 160 ++++++++++++++++++++++++++++++ Libraries/LibGUI/INILexer.h | 89 +++++++++++++++++ Libraries/LibGUI/INISyntaxHighlighter.cpp | 106 ++++++++++++++++++++ Libraries/LibGUI/INISyntaxHighlighter.h | 48 +++++++++ Libraries/LibGUI/Makefile | 2 + Libraries/LibGUI/SyntaxHighlighter.h | 3 +- 6 files changed, 407 insertions(+), 1 deletion(-) create mode 100644 Libraries/LibGUI/INILexer.cpp create mode 100644 Libraries/LibGUI/INILexer.h create mode 100644 Libraries/LibGUI/INISyntaxHighlighter.cpp create mode 100644 Libraries/LibGUI/INISyntaxHighlighter.h diff --git a/Libraries/LibGUI/INILexer.cpp b/Libraries/LibGUI/INILexer.cpp new file mode 100644 index 0000000000..6410a58c03 --- /dev/null +++ b/Libraries/LibGUI/INILexer.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2020, Hüseyin Aslıtürk + * 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 "INILexer.h" +#include +#include + +namespace GUI { + +IniLexer::IniLexer(const StringView& input) + : m_input(input) +{ +} + +char IniLexer::peek(size_t offset) const +{ + if ((m_index + offset) >= m_input.length()) + return 0; + return m_input[m_index + offset]; +} + +char IniLexer::consume() +{ + ASSERT(m_index < m_input.length()); + char ch = m_input[m_index++]; + m_previous_position = m_position; + if (ch == '\n') { + m_position.line++; + m_position.column = 0; + } else { + m_position.column++; + } + return ch; +} + +Vector IniLexer::lex() +{ + Vector tokens; + + size_t token_start_index = 0; + IniPosition token_start_position; + + auto emit_token = [&](auto type) { + IniToken token; + token.m_type = type; + token.m_start = m_position; + token.m_end = m_position; + tokens.append(token); + consume(); + }; + + auto begin_token = [&] { + token_start_index = m_index; + token_start_position = m_position; + }; + + auto commit_token = [&](auto type) { + IniToken token; + token.m_type = type; + token.m_start = token_start_position; + token.m_end = m_previous_position; + tokens.append(token); + }; + + while (m_index < m_input.length()) { + auto ch = peek(); + + if (isspace(ch)) { + begin_token(); + while (isspace(peek())) + consume(); + commit_token(IniToken::Type::Whitespace); + continue; + } + + // ;Comment + if (ch == ';') { + begin_token(); + while (peek() && peek() != '\n') + consume(); + commit_token(IniToken::Type::Comment); + continue; + } + + // [Section] + if (ch == '[') { + // [ Token + begin_token(); + consume(); + commit_token(IniToken::Type::LeftBracket); + + // Section + begin_token(); + while (peek() && !(peek() == ']' || peek() == '\n')) + consume(); + commit_token(IniToken::Type::section); + + // ] Token + if (peek() && peek() == ']') { + begin_token(); + consume(); + commit_token(IniToken::Type::RightBracket); + } + + continue; + } + + // Empty Line + if (ch == '\n') { + consume(); + emit_token(IniToken::Type::Unknown); + continue; + } + + // Name=Value + begin_token(); + while (peek() && !(peek() == '=' || peek() == '\n')) + consume(); + commit_token(IniToken::Type::Name); + + if (peek() && peek() == '=') { + begin_token(); + consume(); + commit_token(IniToken::Type::Equal); + } + + if (peek()) { + begin_token(); + while (peek() && peek() != '\n') + consume(); + commit_token(IniToken::Type::Value); + } + } + return tokens; +} + +} diff --git a/Libraries/LibGUI/INILexer.h b/Libraries/LibGUI/INILexer.h new file mode 100644 index 0000000000..c4f4ba46d8 --- /dev/null +++ b/Libraries/LibGUI/INILexer.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2020, Hüseyin Aslıtürk + * 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 + +namespace GUI { + +#define FOR_EACH_TOKEN_TYPE \ + __TOKEN(Unknown) \ + __TOKEN(Comment) \ + __TOKEN(Whitespace) \ + __TOKEN(section) \ + __TOKEN(LeftBracket) \ + __TOKEN(RightBracket) \ + __TOKEN(Name) \ + __TOKEN(Value) \ + __TOKEN(Equal) + +struct IniPosition { + size_t line; + size_t column; +}; + +struct IniToken { + enum class Type { +#define __TOKEN(x) x, + FOR_EACH_TOKEN_TYPE +#undef __TOKEN + }; + + const char* to_string() const + { + switch (m_type) { +#define __TOKEN(x) \ + case Type::x: \ + return #x; + FOR_EACH_TOKEN_TYPE +#undef __TOKEN + } + ASSERT_NOT_REACHED(); + } + + Type m_type { Type::Unknown }; + IniPosition m_start; + IniPosition m_end; +}; + +class IniLexer { +public: + IniLexer(const StringView&); + + Vector lex(); + +private: + char peek(size_t offset = 0) const; + char consume(); + + StringView m_input; + size_t m_index { 0 }; + IniPosition m_previous_position { 0, 0 }; + IniPosition m_position { 0, 0 }; +}; + +} diff --git a/Libraries/LibGUI/INISyntaxHighlighter.cpp b/Libraries/LibGUI/INISyntaxHighlighter.cpp new file mode 100644 index 0000000000..4ce3f1ac42 --- /dev/null +++ b/Libraries/LibGUI/INISyntaxHighlighter.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2020, Hüseyin Aslıtürk + * 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 +#include +#include +#include +#include + +namespace GUI { + +static TextStyle style_for_token_type(Gfx::Palette palette, IniToken::Type type) +{ + switch (type) { + case IniToken::Type::LeftBracket: + case IniToken::Type::RightBracket: + case IniToken::Type::section: + return { palette.syntax_keyword(), &Gfx::Font::default_bold_fixed_width_font() }; + case IniToken::Type::Name: + return { palette.syntax_identifier() }; + case IniToken::Type::Value: + return { palette.syntax_string() }; + case IniToken::Type::Comment: + return { palette.syntax_comment() }; + case IniToken::Type::Equal: + return { palette.syntax_operator(), &Gfx::Font::default_bold_fixed_width_font() }; + default: + return { palette.base_text() }; + } +} + +bool IniSyntaxHighlighter::is_identifier(void* token) const +{ + auto ini_token = static_cast(reinterpret_cast(token)); + return ini_token == GUI::IniToken::Type::Name; +} + +void IniSyntaxHighlighter::rehighlight(Gfx::Palette palette) +{ + ASSERT(m_editor); + auto text = m_editor->text(); + IniLexer 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.color = style.color; + span.font = style.font; + span.is_skippable = token.m_type == IniToken::Type::Whitespace; + span.data = reinterpret_cast(token.m_type); + spans.append(span); + } + m_editor->document().set_spans(spans); + + m_has_brace_buddies = false; + highlight_matching_token_pair(); + + m_editor->update(); +} + +Vector IniSyntaxHighlighter::matching_token_pairs() const +{ + static Vector pairs; + if (pairs.is_empty()) { + pairs.append({ reinterpret_cast(IniToken::Type::LeftBracket), reinterpret_cast(IniToken::Type::RightBracket) }); + } + return pairs; +} + +bool IniSyntaxHighlighter::token_types_equal(void* token1, void* token2) const +{ + return static_cast(reinterpret_cast(token1)) == static_cast(reinterpret_cast(token2)); +} + +IniSyntaxHighlighter::~IniSyntaxHighlighter() +{ +} + +} diff --git a/Libraries/LibGUI/INISyntaxHighlighter.h b/Libraries/LibGUI/INISyntaxHighlighter.h new file mode 100644 index 0000000000..3425ed9617 --- /dev/null +++ b/Libraries/LibGUI/INISyntaxHighlighter.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020, Hüseyin Aslıtürk + * 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 + +namespace GUI { + +class IniSyntaxHighlighter final : public SyntaxHighlighter { +public: + IniSyntaxHighlighter() {} + virtual ~IniSyntaxHighlighter() override; + + virtual bool is_identifier(void*) const override; + + virtual SyntaxLanguage language() const override { return SyntaxLanguage::INI; } + virtual void rehighlight(Gfx::Palette) override; + +protected: + virtual Vector matching_token_pairs() const override; + virtual bool token_types_equal(void*, void*) const override; +}; + +} diff --git a/Libraries/LibGUI/Makefile b/Libraries/LibGUI/Makefile index 0054949373..c02572d276 100644 --- a/Libraries/LibGUI/Makefile +++ b/Libraries/LibGUI/Makefile @@ -30,6 +30,8 @@ OBJS = \ Icon.o \ InputBox.o \ ItemView.o \ + INILexer.o \ + INISyntaxHighlighter.o \ JsonArrayModel.o \ JSSyntaxHighlighter.o \ Label.o \ diff --git a/Libraries/LibGUI/SyntaxHighlighter.h b/Libraries/LibGUI/SyntaxHighlighter.h index eda27ffead..c95d5a80b3 100644 --- a/Libraries/LibGUI/SyntaxHighlighter.h +++ b/Libraries/LibGUI/SyntaxHighlighter.h @@ -36,7 +36,8 @@ namespace GUI { enum class SyntaxLanguage { PlainText, Cpp, - Javascript + Javascript, + INI }; struct TextStyle { -- cgit v1.2.3