summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorMaciej <sppmacd@pm.me>2023-04-27 12:52:01 +0200
committerSam Atkins <atkinssj@gmail.com>2023-05-05 17:08:40 +0100
commitcf52542fcff37f5cb0d082538fdae2b9c6c85a02 (patch)
tree9e5642cae6c5faac6c6359be1c22c5b326eb1fea /Userland
parent416d6ab6c87adab15b098c1db220aabf1673d9e6 (diff)
downloadserenity-cf52542fcff37f5cb0d082538fdae2b9c6c85a02.zip
LibMarkdown+LibSyntax: Add a Markdown syntax highlighter
It currently supports only headers and code blocks.
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Applications/TextEditor/MainWidget.cpp11
-rw-r--r--Userland/Applications/TextEditor/MainWidget.h1
-rw-r--r--Userland/DevTools/HackStudio/Editor.cpp8
-rw-r--r--Userland/Libraries/LibMarkdown/CMakeLists.txt3
-rw-r--r--Userland/Libraries/LibMarkdown/SyntaxHighlighter.cpp103
-rw-r--r--Userland/Libraries/LibMarkdown/SyntaxHighlighter.h23
-rw-r--r--Userland/Libraries/LibSyntax/Language.cpp10
-rw-r--r--Userland/Libraries/LibSyntax/Language.h1
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,