From fef27ee821fed9a24b731d768b89a0eeb920a4d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szikszai=20Guszt=C3=A1v?= Date: Sat, 23 Sep 2023 20:02:23 +0200 Subject: [PATCH] Implement @highlight-file directive. (#674) --- spec/compilers/directives/highlight-file | 29 +++++++++++++++ spec/examples/directives/highlight_file | 30 +++++++++++++++ spec/fixtures/Test.mint | 5 +++ spec/formatters/directives/highlight-file | 11 ++++++ src/ast/directives/file_based.cr | 3 ++ src/compilers/directives/highlight_file.cr | 26 +++++++++++++ src/formatters/directives/highlight_file.cr | 7 ++++ src/parsers/base_expression.cr | 3 +- src/parsers/directives/highlight_file.cr | 33 +++++++++++++++++ src/scope.cr | 1 + .../directives/highlight_file.cr | 37 +++++++++++++++++++ 11 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 spec/compilers/directives/highlight-file create mode 100644 spec/examples/directives/highlight_file create mode 100644 spec/fixtures/Test.mint create mode 100644 spec/formatters/directives/highlight-file create mode 100644 src/compilers/directives/highlight_file.cr create mode 100644 src/formatters/directives/highlight_file.cr create mode 100644 src/parsers/directives/highlight_file.cr create mode 100644 src/type_checkers/directives/highlight_file.cr diff --git a/spec/compilers/directives/highlight-file b/spec/compilers/directives/highlight-file new file mode 100644 index 000000000..4a79d80d1 --- /dev/null +++ b/spec/compilers/directives/highlight-file @@ -0,0 +1,29 @@ +component Main { + fun render : Html { + @highlight-file(../../fixtures/Test.mint) + } +} +-------------------------------------------------------------------------------- +class A extends _C { + render() { + return _h(React.Fragment, {}, [_h('span', { className: 'keyword' }, [`component`]), + ` `, + _h('span', { className: 'type' }, [`Main`]), + ` { + `, + _h('span', { className: 'keyword' }, [`fun`]), + ` render : `, + _h('span', { className: 'type' }, [`Html`]), + ` { + <`, + _h('span', { className: 'namespace' }, [`div`]), + `> + } +} +`]); + } +}; + +A.displayName = "Main"; diff --git a/spec/examples/directives/highlight_file b/spec/examples/directives/highlight_file new file mode 100644 index 000000000..b58adf7e1 --- /dev/null +++ b/spec/examples/directives/highlight_file @@ -0,0 +1,30 @@ +--------------------------highlight_file_directive_expected_opening_parenthesis +component Main { + fun render : String { + @highlight-file +-----------------------------------------highlight_file_directive_expected_path +component Main { + fun render : String { + @highlight-file( +--------------------------highlight_file_directive_expected_closing_parenthesis +component Main { + fun render : String { + @highlight-file(path +-----------------------------------------highlight_file_directive_expected_file +component Main { + fun render : Html { + @highlight-file(File.mint) + } +} +-----------------------------------------highlight_file_directive_expected_mint +component Main { + fun render : Html { + @highlight-file(../../fixtures/icon-not-svg) + } +} +------------------------------------------------------------------------------- +component Main { + fun render : Html { + @highlight-file(../../../core/source/Array.mint) + } +} diff --git a/spec/fixtures/Test.mint b/spec/fixtures/Test.mint new file mode 100644 index 000000000..697d3e32e --- /dev/null +++ b/spec/fixtures/Test.mint @@ -0,0 +1,5 @@ +component Main { + fun render : Html { +
+ } +} diff --git a/spec/formatters/directives/highlight-file b/spec/formatters/directives/highlight-file new file mode 100644 index 000000000..72d5ee3ae --- /dev/null +++ b/spec/formatters/directives/highlight-file @@ -0,0 +1,11 @@ +component Main { + fun render : Html { + @highlight-file(../../fixtures/Test.mint) + } +} +-------------------------------------------------------------------------------- +component Main { + fun render : Html { + @highlight-file(../../fixtures/Test.mint) + } +} diff --git a/src/ast/directives/file_based.cr b/src/ast/directives/file_based.cr index f3dc67e2b..e2d49af9b 100644 --- a/src/ast/directives/file_based.cr +++ b/src/ast/directives/file_based.cr @@ -49,6 +49,9 @@ module Mint class Svg < FileBased end + + class HighlightFile < FileBased + end end end end diff --git a/src/compilers/directives/highlight_file.cr b/src/compilers/directives/highlight_file.cr new file mode 100644 index 000000000..ab3f0c09e --- /dev/null +++ b/src/compilers/directives/highlight_file.cr @@ -0,0 +1,26 @@ +module Mint + class Compiler + def _compile(node : Ast::Directives::HighlightFile) : String + contents = + File.read(node.real_path) + + parser = Parser.new(contents, node.real_path.to_s) + parser.parse + + parts = + SemanticTokenizer.tokenize(parser.ast) + + mapped = + parts.map do |item| + case item + in String + "`#{skip { escape_for_javascript(item) }}`" + in Tuple(SemanticTokenizer::TokenType, String) + "_h('span', { className: '#{item[0].to_s.underscore}' }, [`#{skip { escape_for_javascript(item[1]) }}`])" + end + end + + "_h(React.Fragment, {}, [#{mapped.join(",\n")}])" + end + end +end diff --git a/src/formatters/directives/highlight_file.cr b/src/formatters/directives/highlight_file.cr new file mode 100644 index 000000000..03f324be9 --- /dev/null +++ b/src/formatters/directives/highlight_file.cr @@ -0,0 +1,7 @@ +module Mint + class Formatter + def format(node : Ast::Directives::HighlightFile) + "@highlight-file(#{node.path})" + end + end +end diff --git a/src/parsers/base_expression.cr b/src/parsers/base_expression.cr index 7e0374743..7139db1d3 100644 --- a/src/parsers/base_expression.cr +++ b/src/parsers/base_expression.cr @@ -30,7 +30,8 @@ module Mint when '`' js when '@' - documentation_directive || + highlight_file_directive || + documentation_directive || highlight_directive || format_directive || inline_directive || diff --git a/src/parsers/directives/highlight_file.cr b/src/parsers/directives/highlight_file.cr new file mode 100644 index 000000000..538f9d822 --- /dev/null +++ b/src/parsers/directives/highlight_file.cr @@ -0,0 +1,33 @@ +module Mint + class Parser + def highlight_file_directive : Ast::Directives::HighlightFile? + parse do |start_position| + next unless word! "@highlight-file" + whitespace + + next error :highlight_file_directive_expected_opening_parenthesis do + expected "the opening parenthesis of an highlight file directive", word + snippet self + end unless char! '(' + whitespace + + next error :highlight_file_directive_expected_path do + expected "the path (to the file) of an highlight file directive", word + snippet self + end unless path = gather { chars { char != ')' } }.presence.try(&.strip) + whitespace + + next error :highlight_file_directive_expected_closing_parenthesis do + expected "the closing parenthesis of an highlight file directive", word + snippet self + end unless char! ')' + + Ast::Directives::HighlightFile.new( + from: start_position, + to: position, + file: file, + path: path) + end + end + end +end diff --git a/src/scope.cr b/src/scope.cr index 18546a10c..b2cf5aec0 100644 --- a/src/scope.cr +++ b/src/scope.cr @@ -216,6 +216,7 @@ module Mint case node when Ast::Directives::Documentation, + Ast::Directives::HighlightFile, Ast::Directives::Highlight, Ast::Directives::Inline, Ast::Directives::Asset, diff --git a/src/type_checkers/directives/highlight_file.cr b/src/type_checkers/directives/highlight_file.cr new file mode 100644 index 000000000..dd5a8b0da --- /dev/null +++ b/src/type_checkers/directives/highlight_file.cr @@ -0,0 +1,37 @@ +module Mint + class TypeChecker + def check(node : Ast::Directives::HighlightFile) : Checkable + error! :highlight_file_directive_expected_file do + block "The path specified for an highlight file directive does not exist: " + + if ENV["SPEC"]? + snippet node.path.to_s + else + snippet node.real_path.to_s + end + + snippet "The highlight file directive in question is here:", node + end unless node.exists? + + contents = + File.read(node.real_path) + + parser = Parser.new(contents, node.real_path.to_s) + parser.parse + parser.eof! + + error! :highlight_file_directive_expected_mint do + block "I was expecting a Mint file for a highlight file directive " \ + "but I could not parse it." + + snippet( + "These are the first few lines of the file:", + contents.lines[0..4].join("\n")) + + snippet "The highlight file directive in question is here:", node + end unless parser.errors.empty? + + HTML + end + end +end