diff options
author | Maciej <sppmacd@pm.me> | 2023-04-27 12:52:01 +0200 |
---|---|---|
committer | Sam Atkins <atkinssj@gmail.com> | 2023-05-05 17:08:40 +0100 |
commit | cf52542fcff37f5cb0d082538fdae2b9c6c85a02 (patch) | |
tree | 9e5642cae6c5faac6c6359be1c22c5b326eb1fea | |
parent | 416d6ab6c87adab15b098c1db220aabf1673d9e6 (diff) | |
download | serenity-cf52542fcff37f5cb0d082538fdae2b9c6c85a02.zip |
LibMarkdown+LibSyntax: Add a Markdown syntax highlighter
It currently supports only headers and code blocks.
-rw-r--r-- | Userland/Applications/TextEditor/MainWidget.cpp | 11 | ||||
-rw-r--r-- | Userland/Applications/TextEditor/MainWidget.h | 1 | ||||
-rw-r--r-- | Userland/DevTools/HackStudio/Editor.cpp | 8 | ||||
-rw-r--r-- | Userland/Libraries/LibMarkdown/CMakeLists.txt | 3 | ||||
-rw-r--r-- | Userland/Libraries/LibMarkdown/SyntaxHighlighter.cpp | 103 | ||||
-rw-r--r-- | Userland/Libraries/LibMarkdown/SyntaxHighlighter.h | 23 | ||||
-rw-r--r-- | Userland/Libraries/LibSyntax/Language.cpp | 10 | ||||
-rw-r--r-- | Userland/Libraries/LibSyntax/Language.h | 1 |
8 files changed, 156 insertions, 4 deletions
diff --git a/Userland/Applications/TextEditor/MainWidget.cpp b/Userland/Applications/TextEditor/MainWidget.cpp index 76571c6f59..94c69fadaa 100644 --- a/Userland/Applications/TextEditor/MainWidget.cpp +++ b/Userland/Applications/TextEditor/MainWidget.cpp @@ -40,6 +40,7 @@ #include <LibGfx/Painter.h> #include <LibJS/SyntaxHighlighter.h> #include <LibMarkdown/Document.h> +#include <LibMarkdown/SyntaxHighlighter.h> #include <LibSQL/AST/SyntaxHighlighter.h> #include <LibWeb/CSS/SyntaxHighlighter/SyntaxHighlighter.h> #include <LibWeb/HTML/SyntaxHighlighter/SyntaxHighlighter.h> @@ -672,6 +673,13 @@ ErrorOr<void> MainWidget::initialize_menubar(GUI::Window& window) syntax_actions.add_action(*m_ini_highlight); TRY(syntax_menu->try_add_action(*m_ini_highlight)); + m_markdown_highlight = GUI::Action::create_checkable("Ma&rkdown", [&](auto&) { + m_editor->set_syntax_highlighter(make<Markdown::SyntaxHighlighter>()); + m_editor->update(); + }); + syntax_actions.add_action(*m_markdown_highlight); + TRY(syntax_menu->try_add_action(*m_markdown_highlight)); + m_shell_highlight = GUI::Action::create_checkable("Sh&ell File", [&](auto&) { m_editor->set_syntax_highlighter(make<Shell::SyntaxHighlighter>()); m_editor->update(); @@ -718,6 +726,7 @@ ErrorOr<void> MainWidget::initialize_menubar(GUI::Window& window) TRY(m_syntax_statusbar_menu->try_add_action(*m_html_highlight)); TRY(m_syntax_statusbar_menu->try_add_action(*m_ini_highlight)); TRY(m_syntax_statusbar_menu->try_add_action(*m_js_highlight)); + TRY(m_syntax_statusbar_menu->try_add_action(*m_markdown_highlight)); TRY(m_syntax_statusbar_menu->try_add_action(*m_shell_highlight)); TRY(m_syntax_statusbar_menu->try_add_action(*m_sql_highlight)); @@ -752,6 +761,8 @@ void MainWidget::set_path(StringView path) m_gml_highlight->activate(); } else if (m_extension == "ini" || m_extension == "af") { m_ini_highlight->activate(); + } else if (m_extension == "md") { + m_markdown_highlight->activate(); } else if (m_extension == "sh" || m_extension == "bash") { m_shell_highlight->activate(); } else if (m_extension == "sql") { diff --git a/Userland/Applications/TextEditor/MainWidget.h b/Userland/Applications/TextEditor/MainWidget.h index 7dde661210..0e338d7e92 100644 --- a/Userland/Applications/TextEditor/MainWidget.h +++ b/Userland/Applications/TextEditor/MainWidget.h @@ -136,6 +136,7 @@ private: RefPtr<GUI::Action> m_git_highlight; RefPtr<GUI::Action> m_gml_highlight; RefPtr<GUI::Action> m_ini_highlight; + RefPtr<GUI::Action> m_markdown_highlight; RefPtr<GUI::Action> m_shell_highlight; RefPtr<GUI::Action> m_sql_highlight; diff --git a/Userland/DevTools/HackStudio/Editor.cpp b/Userland/DevTools/HackStudio/Editor.cpp index 1aa190ef41..4addf01f0e 100644 --- a/Userland/DevTools/HackStudio/Editor.cpp +++ b/Userland/DevTools/HackStudio/Editor.cpp @@ -34,6 +34,7 @@ #include <LibGUI/Window.h> #include <LibJS/SyntaxHighlighter.h> #include <LibMarkdown/Document.h> +#include <LibMarkdown/SyntaxHighlighter.h> #include <LibSQL/AST/SyntaxHighlighter.h> #include <LibSyntax/Language.h> #include <LibWeb/CSS/SyntaxHighlighter/SyntaxHighlighter.h> @@ -653,11 +654,14 @@ void Editor::set_syntax_highlighter_for(CodeDocument const& document) case Syntax::Language::HTML: set_syntax_highlighter(make<Web::HTML::SyntaxHighlighter>()); break; + case Syntax::Language::INI: + set_syntax_highlighter(make<GUI::IniSyntaxHighlighter>()); + break; case Syntax::Language::JavaScript: set_syntax_highlighter(make<JS::SyntaxHighlighter>()); break; - case Syntax::Language::INI: - set_syntax_highlighter(make<GUI::IniSyntaxHighlighter>()); + case Syntax::Language::Markdown: + set_syntax_highlighter(make<Markdown::SyntaxHighlighter>()); break; case Syntax::Language::Shell: set_syntax_highlighter(make<Shell::SyntaxHighlighter>()); diff --git a/Userland/Libraries/LibMarkdown/CMakeLists.txt b/Userland/Libraries/LibMarkdown/CMakeLists.txt index db28d0163a..40571c5208 100644 --- a/Userland/Libraries/LibMarkdown/CMakeLists.txt +++ b/Userland/Libraries/LibMarkdown/CMakeLists.txt @@ -9,9 +9,10 @@ set(SOURCES LineIterator.cpp List.cpp Paragraph.cpp + SyntaxHighlighter.cpp Table.cpp Text.cpp ) serenity_lib(LibMarkdown markdown) -target_link_libraries(LibMarkdown PRIVATE LibJS LibRegex) +target_link_libraries(LibMarkdown PRIVATE LibJS LibRegex LibSyntax) diff --git a/Userland/Libraries/LibMarkdown/SyntaxHighlighter.cpp b/Userland/Libraries/LibMarkdown/SyntaxHighlighter.cpp new file mode 100644 index 0000000000..93a3ffc32a --- /dev/null +++ b/Userland/Libraries/LibMarkdown/SyntaxHighlighter.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2023, Maciej <sppmacd@pm.me> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <LibMarkdown/SyntaxHighlighter.h> + +namespace Markdown { + +Syntax::Language SyntaxHighlighter::language() const +{ + return Syntax::Language::Markdown; +} + +Optional<StringView> SyntaxHighlighter::comment_prefix() const +{ + return {}; +} + +Optional<StringView> SyntaxHighlighter::comment_suffix() const +{ + return {}; +} + +enum class Token { + Default, + Header, + Code +}; + +void SyntaxHighlighter::rehighlight(Palette const& palette) +{ + auto text = m_client->get_text(); + + Vector<GUI::TextDocumentSpan> spans; + + auto append_header = [&](GUI::TextRange const& range) { + Gfx::TextAttributes attributes; + attributes.color = palette.base_text(); + attributes.bold = true; + GUI::TextDocumentSpan span { + .range = range, + .attributes = attributes, + .data = static_cast<u32>(Token::Header), + .is_skippable = false + }; + spans.append(span); + }; + + auto append_code_block = [&](GUI::TextRange const& range) { + Gfx::TextAttributes attributes; + attributes.color = palette.syntax_string(); + GUI::TextDocumentSpan span { + .range = range, + .attributes = attributes, + .data = static_cast<u32>(Token::Code), + .is_skippable = false + }; + spans.append(span); + }; + + // Headers, code blocks + { + size_t line_index = 0; + Optional<size_t> code_block_start; + for (auto const& line : StringView(text).lines()) { + if (line.starts_with("```"sv)) { + if (code_block_start.has_value()) { + append_code_block({ { *code_block_start, 0 }, { line_index, line.length() } }); + code_block_start = {}; + } else { + code_block_start = line_index; + } + } + + if (!code_block_start.has_value()) { + auto trimmed = line.trim_whitespace(TrimMode::Left); + size_t indent = line.length() - trimmed.length(); + if (indent < 4 && trimmed.starts_with("#"sv)) { + append_header({ { line_index, 0 }, { line_index, line.length() } }); + } + } + line_index++; + } + } + + // TODO: Highlight text nodes (em, strong, link, image) + + m_client->do_set_spans(spans); +} + +Vector<SyntaxHighlighter::MatchingTokenPair> SyntaxHighlighter::matching_token_pairs_impl() const +{ + return {}; +} + +bool SyntaxHighlighter::token_types_equal(u64 lhs, u64 rhs) const +{ + return static_cast<Token>(lhs) == static_cast<Token>(rhs); +} + +} diff --git a/Userland/Libraries/LibMarkdown/SyntaxHighlighter.h b/Userland/Libraries/LibMarkdown/SyntaxHighlighter.h new file mode 100644 index 0000000000..0ea40226d9 --- /dev/null +++ b/Userland/Libraries/LibMarkdown/SyntaxHighlighter.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2023, Maciej <sppmacd@pm.me> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <LibSyntax/Highlighter.h> + +namespace Markdown { + +class SyntaxHighlighter : public Syntax::Highlighter { + + virtual Syntax::Language language() const override; + virtual Optional<StringView> comment_prefix() const override; + virtual Optional<StringView> comment_suffix() const override; + virtual void rehighlight(Palette const&) override; + virtual Vector<MatchingTokenPair> matching_token_pairs_impl() const override; + virtual bool token_types_equal(u64, u64) const override; +}; + +} diff --git a/Userland/Libraries/LibSyntax/Language.cpp b/Userland/Libraries/LibSyntax/Language.cpp index f06959453a..16b450302b 100644 --- a/Userland/Libraries/LibSyntax/Language.cpp +++ b/Userland/Libraries/LibSyntax/Language.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2022, the SerenityOS developers. + * Copyright (c) 2020-2023, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause */ @@ -32,6 +32,8 @@ StringView language_to_string(Language language) return "INI"sv; case Language::JavaScript: return "JavaScript"sv; + case Language::Markdown: + return "Markdown"sv; case Language::PlainText: return "Plain Text"sv; case Language::Shell: @@ -63,6 +65,8 @@ StringView common_language_extension(Language language) return "ini"sv; case Language::JavaScript: return "js"sv; + case Language::Markdown: + return "md"sv; case Language::PlainText: return "txt"sv; case Language::Shell: @@ -93,6 +97,8 @@ Optional<Language> language_from_name(StringView name) return Language::INI; if (name.equals_ignoring_ascii_case("JavaScript"sv)) return Language::JavaScript; + if (name.equals_ignoring_ascii_case("Markdown"sv)) + return Language::Markdown; if (name.equals_ignoring_ascii_case("PlainText"sv)) return Language::PlainText; if (name.equals_ignoring_ascii_case("SQL"sv)) @@ -126,6 +132,8 @@ Optional<Language> language_from_filename(LexicalPath const& file) return Language::INI; if (extension.is_one_of("js"sv, "mjs"sv, "json"sv)) return Language::JavaScript; + if (extension == "md"sv) + return Language::Markdown; if (extension.is_one_of("sh"sv, "bash"sv)) return Language::Shell; if (extension == "sql"sv) diff --git a/Userland/Libraries/LibSyntax/Language.h b/Userland/Libraries/LibSyntax/Language.h index 4c78e07ceb..1ba529a474 100644 --- a/Userland/Libraries/LibSyntax/Language.h +++ b/Userland/Libraries/LibSyntax/Language.h @@ -21,6 +21,7 @@ enum class Language { HTML, INI, JavaScript, + Markdown, PlainText, Shell, SQL, |