diff --git a/Applications/TextEditor/TextEditorWidget.cpp b/Applications/TextEditor/TextEditorWidget.cpp index 6325a1a0a0..303e9bb238 100644 --- a/Applications/TextEditor/TextEditorWidget.cpp +++ b/Applications/TextEditor/TextEditorWidget.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -458,6 +459,13 @@ TextEditorWidget::TextEditorWidget() syntax_actions.add_action(*m_js_highlight); syntax_menu.add_action(*m_js_highlight); + m_gml_highlight = GUI::Action::create_checkable("GML", [&](auto&) { + m_editor->set_syntax_highlighter(make()); + m_editor->update(); + }); + syntax_actions.add_action(*m_gml_highlight); + syntax_menu.add_action(*m_gml_highlight); + m_ini_highlight = GUI::Action::create_checkable("INI File", [&](auto&) { m_editor->set_syntax_highlighter(make()); m_editor->update(); @@ -510,6 +518,8 @@ void TextEditorWidget::set_path(const LexicalPath& lexical_path) m_cpp_highlight->activate(); } else if (m_extension == "js" || m_extension == "json") { m_js_highlight->activate(); + } else if (m_extension == "gml") { + m_gml_highlight->activate(); } else if (m_extension == "ini") { m_ini_highlight->activate(); } else { diff --git a/Applications/TextEditor/TextEditorWidget.h b/Applications/TextEditor/TextEditorWidget.h index 70cbadbd57..5b78e84c3c 100644 --- a/Applications/TextEditor/TextEditorWidget.h +++ b/Applications/TextEditor/TextEditorWidget.h @@ -106,6 +106,7 @@ private: RefPtr m_plain_text_highlight; RefPtr m_cpp_highlight; RefPtr m_js_highlight; + RefPtr m_gml_highlight; RefPtr m_ini_highlight; RefPtr m_shell_highlight; diff --git a/DevTools/HackStudio/CodeDocument.cpp b/DevTools/HackStudio/CodeDocument.cpp index 58b3b57035..54f7e4d5f6 100644 --- a/DevTools/HackStudio/CodeDocument.cpp +++ b/DevTools/HackStudio/CodeDocument.cpp @@ -48,6 +48,8 @@ CodeDocument::CodeDocument(const String& file_path, Client* client) m_language = Language::Cpp; else if (lexical_path.has_extension(".js")) m_language = Language::JavaScript; + else if (lexical_path.has_extension(".gml")) + m_language = Language::GML; else if (lexical_path.has_extension(".ini")) m_language = Language::Ini; else if (lexical_path.has_extension(".sh")) diff --git a/DevTools/HackStudio/Editor.cpp b/DevTools/HackStudio/Editor.cpp index 901d1d054a..79f59d403e 100644 --- a/DevTools/HackStudio/Editor.cpp +++ b/DevTools/HackStudio/Editor.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -467,6 +468,9 @@ void Editor::set_document(GUI::TextDocument& doc) set_syntax_highlighter(make()); m_language_client = get_language_client(project().root_path()); break; + case Language::GML: + set_syntax_highlighter(make()); + break; case Language::JavaScript: set_syntax_highlighter(make()); break; diff --git a/DevTools/HackStudio/Language.h b/DevTools/HackStudio/Language.h index 6295fcde25..d763a8ac8a 100644 --- a/DevTools/HackStudio/Language.h +++ b/DevTools/HackStudio/Language.h @@ -31,6 +31,7 @@ enum class Language { Unknown, Cpp, JavaScript, + GML, Ini, Shell, }; diff --git a/Libraries/LibGUI/CMakeLists.txt b/Libraries/LibGUI/CMakeLists.txt index 15b93688c5..c389f76ad5 100644 --- a/Libraries/LibGUI/CMakeLists.txt +++ b/Libraries/LibGUI/CMakeLists.txt @@ -32,6 +32,7 @@ set(SOURCES Frame.cpp GMLLexer.cpp GMLParser.cpp + GMLSyntaxHighlighter.cpp GroupBox.cpp HeaderView.cpp INILexer.cpp diff --git a/Libraries/LibGUI/GMLSyntaxHighlighter.cpp b/Libraries/LibGUI/GMLSyntaxHighlighter.cpp new file mode 100644 index 0000000000..eea5aa3b16 --- /dev/null +++ b/Libraries/LibGUI/GMLSyntaxHighlighter.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2020, Andreas Kling + * 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, GMLToken::Type type) +{ + switch (type) { + case GMLToken::Type::LeftCurly: + case GMLToken::Type::RightCurly: + return { palette.syntax_punctuation() }; + case GMLToken::Type::ClassMarker: + return { palette.syntax_keyword() }; + case GMLToken::Type::ClassName: + return { palette.syntax_identifier(), &Gfx::Font::default_bold_fixed_width_font() }; + case GMLToken::Type::Identifier: + return { palette.syntax_identifier() }; + case GMLToken::Type::JsonValue: + return { palette.syntax_string() }; + case GMLToken::Type::Comment: + return { palette.syntax_comment() }; + default: + return { palette.base_text() }; + } +} + +bool GMLSyntaxHighlighter::is_identifier(void* token) const +{ + auto ini_token = static_cast(reinterpret_cast(token)); + return ini_token == GUI::GMLToken::Type::Identifier; +} + +void GMLSyntaxHighlighter::rehighlight(Gfx::Palette palette) +{ + ASSERT(m_editor); + auto text = m_editor->text(); + GMLLexer 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 = false; + 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 GMLSyntaxHighlighter::matching_token_pairs() const +{ + static Vector pairs; + if (pairs.is_empty()) { + pairs.append({ reinterpret_cast(GMLToken::Type::LeftCurly), reinterpret_cast(GMLToken::Type::RightCurly) }); + } + return pairs; +} + +bool GMLSyntaxHighlighter::token_types_equal(void* token1, void* token2) const +{ + return static_cast(reinterpret_cast(token1)) == static_cast(reinterpret_cast(token2)); +} + +GMLSyntaxHighlighter::~GMLSyntaxHighlighter() +{ +} + +} diff --git a/Libraries/LibGUI/GMLSyntaxHighlighter.h b/Libraries/LibGUI/GMLSyntaxHighlighter.h new file mode 100644 index 0000000000..dc1d73498e --- /dev/null +++ b/Libraries/LibGUI/GMLSyntaxHighlighter.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020, Andreas Kling + * 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 GMLSyntaxHighlighter final : public SyntaxHighlighter { +public: + GMLSyntaxHighlighter() { } + virtual ~GMLSyntaxHighlighter() 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; +}; + +}