Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add HTML::Mason lexer #838

Merged
merged 13 commits into from
Jul 19, 2019
22 changes: 22 additions & 0 deletions lib/rouge/demos/mason
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<%doc>
This is a mason component.
# This is a comment.
</%doc>

<%args>
$color # this argument is required!
$size => 20 # default size
$country => undef # this argument is optional, default value is 'undef'
@items => (1, 2, 'something else')
%pairs => (name => "John", age => 29)
</%args>

% # A random block of Perl code
<%perl>
my @people = ('mary' 'john' 'pete' 'david');
</%perl>

% # Note how each line of code begins with the mandatory %
% foreach my $person (@people) {
Name: <% $person %>
% }
4 changes: 4 additions & 0 deletions lib/rouge/guessers/disambiguation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,11 @@ def match?(filename)
next Mathematica if contains?('(*')
next Mathematica if contains?(':=')

next Mason if matches?(/<%(def|method|text|doc|args|flags|attr|init|once|shared|perl|cleanup|filter)([^>]*)(>)/)

next Matlab if matches?(/^\s*?%/)

next Mason if matches? %r!(</?%|<&)!
end

disambiguate '*.php' do
Expand Down
116 changes: 116 additions & 0 deletions lib/rouge/lexers/mason.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# -*- coding: utf-8 -*- #
# frozen_string_literal: true

module Rouge
module Lexers
class Mason < TemplateLexer
title 'Mason'
desc 'The HTML::Mason framework (https://metacpan.org/pod/HTML::Mason)'
tag 'mason'
filenames '*.mi', '*.mc', '*.mas', '*.m', '*.mhtml', '*.mcomp', 'autohandler', 'dhandler'
mimetypes 'text/x-mason', 'application/x-mason'

def initialize(*)
super
@perl = Perl.new
end

def self.detect?(text)
return false if text.doctype?(/((?:ht|x)ml)/)
return true if text.doctype?
end

# Note: If you add a tag in the lines below, you also need to modify "disambiguate '*.m'" in file disambiguation.rb
TEXT_BLOCKS = %w(text doc)
PERL_BLOCKS = %w(args flags attr init once shared perl cleanup filter strings general_media shared_vars)
COMPONENTS = %w(def method)

state :root do
mixin :mason_tags
end

state :mason_tags do
rule %r/\s+/, Text::Whitespace

rule %r/<%(#{TEXT_BLOCKS.join('|')})>/oi, Comment::Preproc, :text_block

rule %r/<%(#{PERL_BLOCKS.join('|')})>/oi, Comment::Preproc, :perl_block

rule %r/(<%(#{COMPONENTS.join('|')}))([^>]*)(>)/oi do |m|
token Comment::Preproc, m[1]
token Name, m[3]
token Comment::Preproc, m[4]
push :component_block
end

# perl line
rule %r/^(%)(.*)$/ do |m|
token Comment::Preproc, m[1]
delegate @perl, m[2]
end

# start of component call
rule %r/<%/, Comment::Preproc, :component_call

# start of component with content
rule %r/<&\|/ do
token Comment::Preproc
push :component_with_content
push :component_sub
end

# start of component substitution
rule %r/<&/, Comment::Preproc, :component_sub

# fallback to HTML until a mason tag is encountered
rule(/(.+?)(?=(<\/?&|<\/?%|^%|^#))/m) { delegate parent }

# if we get here, there's no more mason tags, so we parse the rest of the doc as HTML
rule(/.+/m) { delegate parent }
end

state :perl_block do
rule %r/<\/%(#{PERL_BLOCKS.join('|')})>/oi, Comment::Preproc, :pop!
rule %r/\s+/, Text::Whitespace
rule %r/^(#.*)$/, Comment
rule(/(.*?[^"])(?=<\/%)/m) { delegate @perl }
end

state :text_block do
rule %r/<\/%(#{TEXT_BLOCKS.join('|')})>/oi, Comment::Preproc, :pop!
rule %r/\s+/, Text::Whitespace
rule %r/^(#.*)$/, Comment
rule %r/(.*?[^"])(?=<\/%)/m, Comment
end

state :component_block do
rule %r/<\/%(#{COMPONENTS.join('|')})>/oi, Comment::Preproc, :pop!
rule %r/\s+/, Text::Whitespace
rule %r/^(#.*)$/, Comment
mixin :mason_tags
end

state :component_with_content do
rule %r/<\/&>/ do
token Comment::Preproc
pop!
end

mixin :mason_tags
end

state :component_sub do
rule %r/&>/, Comment::Preproc, :pop!

rule(/(.*?)(?=&>)/m) { delegate @perl }
end

state :component_call do
rule %r/%>/, Comment::Preproc, :pop!

rule(/(.*?)(?=%>)/m) { delegate @perl }
end
end
end
end

43 changes: 43 additions & 0 deletions spec/lexers/mason_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# -*- coding: utf-8 -*- #

describe Rouge::Lexers::Mason do
let(:subject) { Rouge::Lexers::Mason.new }

describe 'guessing' do
include Support::Guessing

it 'guesses by filename' do
assert_guess :filename => 'foo.mas'
assert_guess :filename => 'foo.mi'
assert_guess :filename => 'foo.mc'
assert_guess :filename => 'foo.mhtml'
assert_guess :filename => 'foo.mcomp'
assert_guess :filename => 'autohandler'
assert_guess :filename => 'dhandler'
end

it 'guesses by mimetype' do
assert_guess :mimetype => 'text/x-mason'
assert_guess :mimetype => 'application/x-mason'
end

it 'guesses by source' do
assert_guess :filename => 'foo.m', :source => <<-eos
<%doc>
miparnisari marked this conversation as resolved.
Show resolved Hide resolved
This is a mason component.
</%doc>

eos

end

it 'guesses by source when using component calls' do
assert_guess :filename => 'foo.m', :source => <<-eos
<& 'SELF:component' &>

eos

end

end
end
18 changes: 18 additions & 0 deletions spec/lexers/matlab_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,24 @@
assert_guess :mimetype => 'text/x-matlab'
assert_guess :mimetype => 'application/x-matlab'
end

it 'guesses by source' do
assert_guess :filename => 'foo.m', :source => <<-eos
miparnisari marked this conversation as resolved.
Show resolved Hide resolved
function ImageGaborPhase(TFType,pkt,ell,titlestr)
% ImageGaborPhase -- Time-Frequency Display with congruent rectangles

eos
end

it 'guesses by source' do
assert_guess :filename => 'foo.m', :source => <<-eos
miparnisari marked this conversation as resolved.
Show resolved Hide resolved
methods
function obj = GaussianBlur(sigma, img_size, edges)
%GAUSSIANBLUR Create Gaussian Blur filter object
end
end
eos
end
end
end

Loading