middleman-vegas 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +80 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/middleman-vegas.rb +5 -0
- data/lib/middleman-vegas/extension.rb +60 -0
- data/lib/middleman-vegas/formatters/table_formatter.rb +81 -0
- data/lib/middleman-vegas/highlighter.rb +50 -0
- data/lib/middleman-vegas/lexers/habitat_studio.rb +164 -0
- data/lib/middleman-vegas/markdown_parser.rb +62 -0
- data/lib/middleman-vegas/options_parser.rb +140 -0
- data/lib/middleman-vegas/redcarpet_code_renderer.rb +20 -0
- data/lib/middleman-vegas/version.rb +5 -0
- data/middleman-vegas.gemspec +29 -0
- metadata +120 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 11a6e075c85ec4bc5795feb0a5cf8b1e6072cb59
|
4
|
+
data.tar.gz: f3bf9881353914c84facb586e40a38ff36b40434
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9117b96ee612425ef25d031e17bbe094a4795400ba6655a789f7ae1e238b7d3aa028bd861bbc5f70957384f9eac638aa94c900c1dbb9631bc49e110cc1f6766c
|
7
|
+
data.tar.gz: a74bca176f8b0b66df18e46dc95b2da0302f1dfa95b9bb27cd5461cd31bdb5759d9a1e89b1ef22c6fbc557fcc6b1663587e71d937b27217a1606f51b1c14367d
|
data/.gitignore
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at franklin.webber@gmail.com. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [http://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: http://contributor-covenant.org
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Franklin Webber
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# Middleman::Vegas
|
2
|
+
|
3
|
+
This brings the great styles and metadata support found in the Octopress Code Highlighter to Middleman. This has some additional features beyond that to support the work being done for Habitat.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'middleman-vegas'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install middleman-vegas
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
Activate it to the Middleman `config.rb`:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
activate :vegas
|
27
|
+
```
|
28
|
+
|
29
|
+
## Syntax
|
30
|
+
|
31
|
+
You can define code fences as you would normally. These code fences have support for the languages defined in [rouge](https://github.com/jneen/rouge/wiki/List-of-supported-languages-and-lexers).
|
32
|
+
|
33
|
+
Define a code fence with a language.
|
34
|
+
|
35
|
+
```lang
|
36
|
+
[code]
|
37
|
+
```
|
38
|
+
|
39
|
+
Define a code fence for a language with the addition of a title.
|
40
|
+
|
41
|
+
```lang title
|
42
|
+
[code]
|
43
|
+
```
|
44
|
+
|
45
|
+
Define a code fence for a language with additional metadata
|
46
|
+
|
47
|
+
```lang [metadata]
|
48
|
+
[code]
|
49
|
+
```
|
50
|
+
|
51
|
+
The additional metadata that can be specified:
|
52
|
+
|
53
|
+
| Metadata | Example | Description |
|
54
|
+
|:-------------|:---------------------------|:----------------------------------------------------------------------|
|
55
|
+
|`lang` | `ruby` | Used by the syntax highlighter. Passing 'plain' disables highlighting.|
|
56
|
+
|`title` | `title:"Figure 1.A"` | Add a figcaption title to your code block. |
|
57
|
+
|`url` | `url:"https://github.com"` | No default value |
|
58
|
+
|`link_text` | `link_text:"Download"` | Text for the link, default: `"link"` |
|
59
|
+
|`linenos` | `linenos:true` | Enables line numbering |
|
60
|
+
|`start` | `start:5` | Start the line numbering at the given value. |
|
61
|
+
|`mark` | `mark:1-4,8` | Highlight lines of code. This example marks lines 1,2,3,4 and 8 |
|
62
|
+
|`class` | `class:"css example"` | Add CSS class names to the code `<figure>` element |
|
63
|
+
|
64
|
+
## Development
|
65
|
+
|
66
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
67
|
+
|
68
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
69
|
+
|
70
|
+
## Contributing
|
71
|
+
|
72
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/burtlo/middleman-vegas. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
73
|
+
|
74
|
+
## License
|
75
|
+
|
76
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
77
|
+
|
78
|
+
## Code of Conduct
|
79
|
+
|
80
|
+
Everyone interacting in the Middleman::Octopress::Code::Highlighter project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/burtlo/middleman-vegas/blob/master/CODE_OF_CONDUCT.md).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "middleman/octopress/code/highlighter"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'middleman-vegas/highlighter'
|
2
|
+
require 'middleman-vegas/redcarpet_code_renderer'
|
3
|
+
|
4
|
+
module Middleman
|
5
|
+
module Vegas
|
6
|
+
class SyntaxExtension < ::Middleman::Extension
|
7
|
+
option :css_class, 'highlight', 'Class name applied to the syntax-highlighted output.'
|
8
|
+
option :line_numbers, false, 'Generate line numbers.'
|
9
|
+
option :start_line, 1, 'Start the line numbering (if enabled) at the desired integer'
|
10
|
+
option :inline_theme, nil, 'A Rouge::CSSTheme that will be used to highlight the output with inline styles instead of using CSS classes.'
|
11
|
+
option :wrap, true, 'Wrap the highlighted content in a container (<pre> or <div>, depending on whether :line_numbers is on).'
|
12
|
+
option :lexer_options, {}, 'Options for the Rouge lexers.'
|
13
|
+
|
14
|
+
def after_configuration
|
15
|
+
Middleman::Vegas::Highlighter.options = options
|
16
|
+
if app.config[:markdown_engine] == :redcarpet
|
17
|
+
require 'middleman-core/renderers/redcarpet'
|
18
|
+
Middleman::Renderers::MiddlemanRedcarpetHTML.send :include, RedcarpetCodeRenderer
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
helpers do
|
23
|
+
# Output highlighted code. Use like:
|
24
|
+
#
|
25
|
+
# <% code('ruby', :line_numbers => true, :start_line => 7) do %>
|
26
|
+
# my code
|
27
|
+
# <% end %>
|
28
|
+
#
|
29
|
+
# To produce the following structure:
|
30
|
+
#
|
31
|
+
# <pre class="highlight ruby">
|
32
|
+
# <code>#{your code}</code>
|
33
|
+
# </pre>
|
34
|
+
#
|
35
|
+
# If no language is provided, then the language name is `plaintext`.
|
36
|
+
#
|
37
|
+
# @param [String] language that the Rouge lexer should use
|
38
|
+
# @param [Hash] Options to pass to the Rouge formatter & lexer, overriding global options set by :highlighter_options.
|
39
|
+
def code(language=nil, options={}, &block)
|
40
|
+
raise 'The code helper requires a block to be provided.' unless block_given?
|
41
|
+
|
42
|
+
options[:lang] = language.to_s
|
43
|
+
options[:marks] ||= []
|
44
|
+
|
45
|
+
# Save current buffer for later
|
46
|
+
@_out_buf, _buf_was = "", @_out_buf
|
47
|
+
|
48
|
+
begin
|
49
|
+
content = capture_html(&block)
|
50
|
+
ensure
|
51
|
+
# Reset stored buffer
|
52
|
+
@_out_buf = _buf_was
|
53
|
+
end
|
54
|
+
content = content.encode(Encoding::UTF_8)
|
55
|
+
concat_content Highlighter.highlight(content, options).html_safe
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Middleman
|
2
|
+
module Vegas
|
3
|
+
|
4
|
+
# When you want to render code with a title, a link, line numbers and highlights
|
5
|
+
# in a table style.
|
6
|
+
class TableFormatter
|
7
|
+
def render(code, metadata)
|
8
|
+
lexer = Rouge::Lexer.find_fancy(metadata[:lang], code) || Rouge::Lexers::PlainText
|
9
|
+
lexed_code = expand_tokens_with_newlines(lexer.lex(code, {}))
|
10
|
+
|
11
|
+
formatter = Rouge::Formatters::HTML.new(wrap: false)
|
12
|
+
rendered_code = formatter.format(lexed_code)
|
13
|
+
rendered_code = tableize_code(rendered_code, metadata)
|
14
|
+
|
15
|
+
classnames = [ 'code-highlight-figure', metadata[:class].to_s ].join(' ')
|
16
|
+
|
17
|
+
"<figure class='#{classnames}'>#{caption(metadata)}#{rendered_code}</figure>"
|
18
|
+
end
|
19
|
+
|
20
|
+
# The lexed code generates an enumerator of tokens with their values.
|
21
|
+
# Before they are rendered to HTML all of the non-text tokens with newlines
|
22
|
+
# should be split into several tokens of the same type. This ensures
|
23
|
+
# that when they are tableized later the surrounding spans are not broken.
|
24
|
+
def expand_tokens_with_newlines(lexed_code)
|
25
|
+
full_lex = []
|
26
|
+
lexed_code.each do |token, value|
|
27
|
+
if token.qualname == "Text"
|
28
|
+
full_lex << [ token, value ]
|
29
|
+
else
|
30
|
+
lines = value.split("\n")
|
31
|
+
lines.each_with_index do |line, index|
|
32
|
+
# if not the last line or the last line had a newline at the end
|
33
|
+
suffix = if index < (lines.length - 1) || (index == (lines.length - 1) && value.end_with?("\n"))
|
34
|
+
"\n"
|
35
|
+
else
|
36
|
+
""
|
37
|
+
end
|
38
|
+
|
39
|
+
full_lex << [ token, "#{line}#{suffix}" ]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
full_lex
|
44
|
+
end
|
45
|
+
|
46
|
+
# Given the rendered code it is time to present the information in a table.
|
47
|
+
def tableize_code(code, options)
|
48
|
+
start = options[:start] || 1
|
49
|
+
lines = options[:linenos] || false
|
50
|
+
marks = options[:marks]
|
51
|
+
|
52
|
+
table = "<div class='code-highlight'>"
|
53
|
+
table += "<pre class='code-highlight-pre'>"
|
54
|
+
code.lines.each_with_index do |line,index|
|
55
|
+
classes = 'code-highlight-row'
|
56
|
+
classes += lines ? ' numbered' : ' unnumbered'
|
57
|
+
if marks.include? index + start
|
58
|
+
classes += ' marked-line'
|
59
|
+
classes += ' start-marked-line' unless marks.include? index - 1 + start
|
60
|
+
classes += ' end-marked-line' unless marks.include? index + 1 + start
|
61
|
+
end
|
62
|
+
line = line.strip.empty? ? ' ' : line
|
63
|
+
table += "<div data-line='#{index + start}' class='#{classes}'><div class='code-highlight-line'>#{line}</div></div>"
|
64
|
+
end
|
65
|
+
table +="</pre></div>"
|
66
|
+
end
|
67
|
+
|
68
|
+
# Generates a caption above the code area when there is a title / url
|
69
|
+
def caption(options)
|
70
|
+
if options[:title]
|
71
|
+
figcaption = "<figcaption class='code-highlight-caption'><span class='code-highlight-caption-title'>#{options[:title]}</span>"
|
72
|
+
figcaption += "<a class='code-highlight-caption-link' href='#{options[:url]}'>#{(options[:link_text] || 'link').strip}</a>" if options[:url]
|
73
|
+
figcaption += "</figcaption>"
|
74
|
+
else
|
75
|
+
''
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'rouge'
|
2
|
+
require 'middleman-vegas/formatters/table_formatter'
|
3
|
+
|
4
|
+
module Middleman
|
5
|
+
module Vegas
|
6
|
+
module Highlighter
|
7
|
+
mattr_accessor :options
|
8
|
+
|
9
|
+
# The highlight method is called when code fences are used in RedCarpet
|
10
|
+
# and when the code helper is used.
|
11
|
+
#
|
12
|
+
# @param code [String] the content found within the code block
|
13
|
+
# @param options [Hash] contains any additional rendering options provided
|
14
|
+
# to the code helper methods, as code fences don't have a way to
|
15
|
+
# provide additional parameters.
|
16
|
+
#
|
17
|
+
# @return the HTML that will be rendered to the page
|
18
|
+
def self.highlight(code, metadata={})
|
19
|
+
return no_html if code_block_is_empty?(code.strip)
|
20
|
+
metadata[:lang] = with_lang_aliases_considered(metadata[:lang])
|
21
|
+
TableFormatter.new.render(code, metadata)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.code_block_is_empty?(code)
|
25
|
+
code == "" || code == "</div>"
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.no_html
|
29
|
+
""
|
30
|
+
end
|
31
|
+
|
32
|
+
# When languages are provided they could be aliases for other languages
|
33
|
+
# or the way that they are presented. With a few languages we want to
|
34
|
+
# make sure that they are presented within the context of a console.
|
35
|
+
def self.with_lang_aliases_considered(lang)
|
36
|
+
case lang
|
37
|
+
when 'cmd'
|
38
|
+
'console?lang=powershell'
|
39
|
+
when 'posh', 'powershell', 'shell', 'studio'
|
40
|
+
"console?lang=#{lang}"
|
41
|
+
when 'ps1'
|
42
|
+
'powershell'
|
43
|
+
else
|
44
|
+
lang
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
require 'rouge'
|
2
|
+
|
3
|
+
module Middleman
|
4
|
+
module Vegas
|
5
|
+
class HabitatStudio < Rouge::RegexLexer
|
6
|
+
title "studio"
|
7
|
+
desc "Habitat Studio uses bash but there are a few binaries, aliases, and functions that already exist"
|
8
|
+
|
9
|
+
tag 'studio'
|
10
|
+
|
11
|
+
KEYWORDS = %w(
|
12
|
+
if fi else while do done for then return function
|
13
|
+
select continue until esac elif in
|
14
|
+
).join('|')
|
15
|
+
|
16
|
+
BUILTINS = %w(
|
17
|
+
hab build sl sup-log
|
18
|
+
).join('|')
|
19
|
+
|
20
|
+
state :basic do
|
21
|
+
rule /#.*$/, Comment
|
22
|
+
|
23
|
+
rule /\b(#{KEYWORDS})\s*\b/, Keyword
|
24
|
+
rule /\bcase\b/, Keyword, :case
|
25
|
+
|
26
|
+
rule /\b(#{BUILTINS})\s*\b(?!(\.|-))/, Name::Builtin
|
27
|
+
rule /[.](?=\s)/, Name::Builtin
|
28
|
+
|
29
|
+
rule /(\b\w+)(=)/ do |m|
|
30
|
+
groups Name::Variable, Operator
|
31
|
+
end
|
32
|
+
|
33
|
+
rule /[\[\]{}()!=>]/, Operator
|
34
|
+
rule /&&|\|\|/, Operator
|
35
|
+
|
36
|
+
# here-string
|
37
|
+
rule /<<</, Operator
|
38
|
+
|
39
|
+
rule /(<<-?)(\s*)(\'?)(\\?)(\w+)(\3)/ do |m|
|
40
|
+
groups Operator, Text, Str::Heredoc, Str::Heredoc, Name::Constant, Str::Heredoc
|
41
|
+
@heredocstr = Regexp.escape(m[5])
|
42
|
+
push :heredoc
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
state :heredoc do
|
47
|
+
rule /\n/, Str::Heredoc, :heredoc_nl
|
48
|
+
rule /[^$\n]+/, Str::Heredoc
|
49
|
+
mixin :interp
|
50
|
+
rule /[$]/, Str::Heredoc
|
51
|
+
end
|
52
|
+
|
53
|
+
state :heredoc_nl do
|
54
|
+
rule /\s*(\w+)\s*\n/ do |m|
|
55
|
+
if m[1] == @heredocstr
|
56
|
+
token Name::Constant
|
57
|
+
pop! 2
|
58
|
+
else
|
59
|
+
token Str::Heredoc
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
rule(//) { pop! }
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
state :double_quotes do
|
68
|
+
# NB: "abc$" is literally the string abc$.
|
69
|
+
# Here we prevent :interp from interpreting $" as a variable.
|
70
|
+
rule /(?:\$#?)?"/, Str::Double, :pop!
|
71
|
+
mixin :interp
|
72
|
+
rule /[^"`\\$]+/, Str::Double
|
73
|
+
end
|
74
|
+
|
75
|
+
state :ansi_string do
|
76
|
+
rule /\\./, Str::Escape
|
77
|
+
rule /[^\\']+/, Str::Single
|
78
|
+
mixin :single_quotes
|
79
|
+
end
|
80
|
+
|
81
|
+
state :single_quotes do
|
82
|
+
rule /'/, Str::Single, :pop!
|
83
|
+
rule /[^']+/, Str::Single
|
84
|
+
end
|
85
|
+
|
86
|
+
state :data do
|
87
|
+
rule /\s+/, Text
|
88
|
+
rule /\\./, Str::Escape
|
89
|
+
rule /\$?"/, Str::Double, :double_quotes
|
90
|
+
rule /\$'/, Str::Single, :ansi_string
|
91
|
+
|
92
|
+
# single quotes are much easier than double quotes - we can
|
93
|
+
# literally just scan until the next single quote.
|
94
|
+
# POSIX: Enclosing characters in single-quotes ( '' )
|
95
|
+
# shall preserve the literal value of each character within the
|
96
|
+
# single-quotes. A single-quote cannot occur within single-quotes.
|
97
|
+
rule /'/, Str::Single, :single_quotes
|
98
|
+
|
99
|
+
rule /\*/, Keyword
|
100
|
+
|
101
|
+
rule /;/, Punctuation
|
102
|
+
|
103
|
+
rule /--?[\w-]+/, Name::Tag
|
104
|
+
rule /[^=\*\s{}()$"'`;\\<]+/, Text
|
105
|
+
rule /\d+(?= |\Z)/, Num
|
106
|
+
rule /</, Text
|
107
|
+
mixin :interp
|
108
|
+
end
|
109
|
+
|
110
|
+
state :curly do
|
111
|
+
rule /}/, Keyword, :pop!
|
112
|
+
rule /:-/, Keyword
|
113
|
+
rule /[a-zA-Z0-9_]+/, Name::Variable
|
114
|
+
rule /[^}:"`'$]+/, Punctuation
|
115
|
+
mixin :root
|
116
|
+
end
|
117
|
+
|
118
|
+
state :paren do
|
119
|
+
rule /\)/, Keyword, :pop!
|
120
|
+
mixin :root
|
121
|
+
end
|
122
|
+
|
123
|
+
state :math do
|
124
|
+
rule /\)\)/, Keyword, :pop!
|
125
|
+
rule %r([-+*/%^|&!]|\*\*|\|\|), Operator
|
126
|
+
rule /\d+(#\w+)?/, Num
|
127
|
+
mixin :root
|
128
|
+
end
|
129
|
+
|
130
|
+
state :case do
|
131
|
+
rule /\besac\b/, Keyword, :pop!
|
132
|
+
rule /\|/, Punctuation
|
133
|
+
rule /\)/, Punctuation, :case_stanza
|
134
|
+
mixin :root
|
135
|
+
end
|
136
|
+
|
137
|
+
state :case_stanza do
|
138
|
+
rule /;;/, Punctuation, :pop!
|
139
|
+
mixin :root
|
140
|
+
end
|
141
|
+
|
142
|
+
state :backticks do
|
143
|
+
rule /`/, Str::Backtick, :pop!
|
144
|
+
mixin :root
|
145
|
+
end
|
146
|
+
|
147
|
+
state :interp do
|
148
|
+
rule /\\$/, Str::Escape # line continuation
|
149
|
+
rule /\\./, Str::Escape
|
150
|
+
rule /\$\(\(/, Keyword, :math
|
151
|
+
rule /\$\(/, Keyword, :paren
|
152
|
+
rule /\${#?/, Keyword, :curly
|
153
|
+
rule /`/, Str::Backtick, :backticks
|
154
|
+
rule /\$#?(\w+|.)/, Name::Variable
|
155
|
+
rule /\$[*@]/, Name::Variable
|
156
|
+
end
|
157
|
+
|
158
|
+
state :root do
|
159
|
+
mixin :basic
|
160
|
+
mixin :data
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'middleman-vegas/options_parser'
|
2
|
+
|
3
|
+
module Middleman
|
4
|
+
module Vegas
|
5
|
+
module MarkdownParser
|
6
|
+
# Evaluate the entire document and look code blocks:
|
7
|
+
#
|
8
|
+
# ```LANGUAGE METADATA
|
9
|
+
# CODE
|
10
|
+
# ```
|
11
|
+
def self.parse_document(full_document)
|
12
|
+
full_document.gsub /^`{3}.+?`{3}/m do |code_block|
|
13
|
+
parse_code_block code_block
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Extract from the code block the metadata and the code and highlight it.
|
18
|
+
#
|
19
|
+
# ```LANGUAGE METADATA
|
20
|
+
# CODE
|
21
|
+
# ```
|
22
|
+
#
|
23
|
+
def self.parse_code_block(code_block)
|
24
|
+
code_block.gsub /`{3}([^\n]+)?\n(.+?)`{3}\Z/m do
|
25
|
+
metadata = get_metadata(Regexp.last_match(1).to_s)
|
26
|
+
code = Regexp.last_match(2).to_s
|
27
|
+
::Middleman::Vegas::Highlighter.highlight(code, metadata)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
AllOptions = /([^\s]+)\s+(.+?)\s+(https?:\/\/\S+|\/\S+)\s*(.+)?/i
|
32
|
+
LangCaption = /([^\s]+)\s*(.+)?/i
|
33
|
+
|
34
|
+
# Extract the metadata from the code block. There are two simple formats:
|
35
|
+
#
|
36
|
+
# ```LANGUAGE TITLE
|
37
|
+
#
|
38
|
+
# ```LANGUAGE TITLE URL LINK_TEXT
|
39
|
+
#
|
40
|
+
#
|
41
|
+
def self.get_metadata(markup)
|
42
|
+
defaults = { escape: true }
|
43
|
+
clean_markup = OptionsParser.new(markup).clean_markup
|
44
|
+
|
45
|
+
if clean_markup =~ AllOptions
|
46
|
+
defaults = {
|
47
|
+
lang: $1,
|
48
|
+
title: $2,
|
49
|
+
url: $3,
|
50
|
+
link_text: $4,
|
51
|
+
}
|
52
|
+
elsif clean_markup =~ LangCaption
|
53
|
+
defaults = {
|
54
|
+
lang: $1,
|
55
|
+
title: $2
|
56
|
+
}
|
57
|
+
end
|
58
|
+
OptionsParser.new(markup).parse_markup(defaults)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
module Middleman
|
2
|
+
module Vegas
|
3
|
+
class OptionsParser
|
4
|
+
attr_accessor :input
|
5
|
+
|
6
|
+
def initialize(markup)
|
7
|
+
@input = markup.strip
|
8
|
+
end
|
9
|
+
|
10
|
+
def clean_markup
|
11
|
+
input.sub(/\s*lang:\s*\S+/i,'')
|
12
|
+
.sub(/\s*title:\s*(("(.+?)")|('(.+?)')|(\S+))/i,'')
|
13
|
+
.sub(/\s*url:\s*(\S+)/i,'')
|
14
|
+
.sub(/\s*link_text:\s*(("(.+?)")|('(.+?)')|(\S+))/i,'')
|
15
|
+
.sub(/\s*mark:\s*\d\S*/i,'')
|
16
|
+
.sub(/\s*linenos:\s*\w+/i,'')
|
17
|
+
.sub(/\s*start:\s*\d+/i,'')
|
18
|
+
.sub(/\s*end:\s*\d+/i,'')
|
19
|
+
.sub(/\s*range:\s*\d+-\d+/i,'')
|
20
|
+
.sub(/\s*escape:\s*\w+/i,'')
|
21
|
+
.sub(/\s*startinline:\s*\w+/i,'')
|
22
|
+
.sub(/\s*class:\s*(("(.+?)")|('(.+?)')|(\S+))/i,'')
|
23
|
+
end
|
24
|
+
|
25
|
+
def parse_markup(defaults = {})
|
26
|
+
options = {
|
27
|
+
lang: lang,
|
28
|
+
url: url,
|
29
|
+
title: title,
|
30
|
+
linenos: linenos,
|
31
|
+
marks: marks,
|
32
|
+
link_text: link_text,
|
33
|
+
start: start,
|
34
|
+
end: endline,
|
35
|
+
escape: escape,
|
36
|
+
startinline: startinline,
|
37
|
+
class: classnames
|
38
|
+
}
|
39
|
+
options = options.delete_if { |k,v| v.nil? }
|
40
|
+
defaults.merge(options)
|
41
|
+
end
|
42
|
+
|
43
|
+
def lang
|
44
|
+
extract(/\s*lang:\s*(\S+)/i)
|
45
|
+
end
|
46
|
+
|
47
|
+
def startinline
|
48
|
+
boolize(extract(/\s*startinline:\s*(\w+)/i))
|
49
|
+
end
|
50
|
+
|
51
|
+
def classnames
|
52
|
+
extract(/\s*class:\s*(("(.+?)")|('(.+?)')|(\S+))/i, [3, 5, 6])
|
53
|
+
end
|
54
|
+
|
55
|
+
def url
|
56
|
+
extract(/\s*url:\s*(("(.+?)")|('(.+?)')|(\S+))/i, [3, 5, 6])
|
57
|
+
end
|
58
|
+
|
59
|
+
def title
|
60
|
+
extract(/\s*title:\s*(("(.+?)")|('(.+?)')|(\S+))/i, [3, 5, 6])
|
61
|
+
end
|
62
|
+
|
63
|
+
def linenos
|
64
|
+
boolize(extract(/\s*linenos:\s*(\w+)/i))
|
65
|
+
end
|
66
|
+
|
67
|
+
def escape
|
68
|
+
boolize(extract(/\s*escape:\s*(\w+)/i))
|
69
|
+
end
|
70
|
+
|
71
|
+
# Public: Matches pattern for line marks and returns array of line
|
72
|
+
# numbers to mark
|
73
|
+
#
|
74
|
+
# Example input
|
75
|
+
# Input: "mark:1,5-10,2"
|
76
|
+
# Output: [1,2,5,6,7,8,9,10]
|
77
|
+
#
|
78
|
+
# Returns an array of integers corresponding to the lines which are
|
79
|
+
# indicated as marked
|
80
|
+
def marks
|
81
|
+
marks = []
|
82
|
+
if input =~ / *mark:(\d\S*)/i
|
83
|
+
marks = $1.gsub /(\d+)-(\d+)/ do
|
84
|
+
($1.to_i..$2.to_i).to_a.join(',')
|
85
|
+
end
|
86
|
+
marks = marks.split(',').collect {|s| s.to_i}.sort
|
87
|
+
end
|
88
|
+
marks
|
89
|
+
end
|
90
|
+
|
91
|
+
def link_text
|
92
|
+
extract(/\s*link[-_]text:\s*(("(.+?)")|('(.+?)')|(\S+))/i, [3, 5, 6], 'link')
|
93
|
+
end
|
94
|
+
|
95
|
+
def start
|
96
|
+
if range
|
97
|
+
range.first
|
98
|
+
else
|
99
|
+
num = extract(/\s*start:\s*(\d+)/i)
|
100
|
+
num = num.to_i unless num.nil?
|
101
|
+
num
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def endline
|
106
|
+
if range
|
107
|
+
range.last
|
108
|
+
else
|
109
|
+
num = extract(/\s*end:\s*(\d+)/i)
|
110
|
+
num = num.to_i unless num.nil?
|
111
|
+
num
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def range
|
116
|
+
if input.match(/ *range:(\d+)-(\d+)/i)
|
117
|
+
[$1.to_i, $2.to_i]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def extract(regexp, indices_to_try = [1], default = nil)
|
122
|
+
thing = input.match(regexp)
|
123
|
+
if thing.nil?
|
124
|
+
default
|
125
|
+
else
|
126
|
+
indices_to_try.each do |index|
|
127
|
+
return thing[index] if thing[index]
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def boolize(str)
|
133
|
+
return nil if str.nil?
|
134
|
+
return true if str == true || str =~ (/(true|t|yes|y|1)$/i)
|
135
|
+
return false if str == false || str =~ (/(false|f|no|n|0)$/i) || str.strip.size > 1
|
136
|
+
return str
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'middleman-vegas/markdown_parser'
|
2
|
+
|
3
|
+
module Middleman
|
4
|
+
module Vegas
|
5
|
+
# A mixin for the Redcarpet Markdown renderer that will assist in finding
|
6
|
+
# codeblocks and replacing them with rendered HTML.
|
7
|
+
module RedcarpetCodeRenderer
|
8
|
+
#
|
9
|
+
# Traditionally you would enable code fences in RedCarpet and then
|
10
|
+
# process it with #block_code function. But code blocks parsed that way
|
11
|
+
# will not allow you to define metadata. So the entire document needs to
|
12
|
+
# be examined.
|
13
|
+
#
|
14
|
+
# @see Middleman::Vegas::MarkdownParser
|
15
|
+
def preprocess(full_document)
|
16
|
+
MarkdownParser.parse_document full_document
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "middleman-vegas/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "middleman-vegas"
|
8
|
+
spec.version = Middleman::Vegas::VERSION
|
9
|
+
spec.authors = ["Franklin Webber"]
|
10
|
+
spec.email = ["franklin.webber@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Add code highlighting to your middleman application.}
|
13
|
+
spec.description = %q{This brings the powerful features found in the Octopress Code Highlighter to Middleman. This allows you to specify code fences with additional metadata to provide a richer experience when using code to tell your stories.}
|
14
|
+
spec.homepage = "https://github.com/burtlo/middleman-vegas"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
18
|
+
f.match(%r{^(test|spec|features)/})
|
19
|
+
end
|
20
|
+
spec.bindir = "exe"
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_runtime_dependency("middleman-core", ["~> 4.0"])
|
25
|
+
spec.add_runtime_dependency("rouge", ["~> 3.0"])
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.15"
|
27
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
28
|
+
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: middleman-vegas
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Franklin Webber
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-11-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: middleman-core
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rouge
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.15'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.15'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.0'
|
69
|
+
description: This brings the powerful features found in the Octopress Code Highlighter
|
70
|
+
to Middleman. This allows you to specify code fences with additional metadata to
|
71
|
+
provide a richer experience when using code to tell your stories.
|
72
|
+
email:
|
73
|
+
- franklin.webber@gmail.com
|
74
|
+
executables: []
|
75
|
+
extensions: []
|
76
|
+
extra_rdoc_files: []
|
77
|
+
files:
|
78
|
+
- ".gitignore"
|
79
|
+
- CODE_OF_CONDUCT.md
|
80
|
+
- Gemfile
|
81
|
+
- LICENSE.txt
|
82
|
+
- README.md
|
83
|
+
- Rakefile
|
84
|
+
- bin/console
|
85
|
+
- bin/setup
|
86
|
+
- lib/middleman-vegas.rb
|
87
|
+
- lib/middleman-vegas/extension.rb
|
88
|
+
- lib/middleman-vegas/formatters/table_formatter.rb
|
89
|
+
- lib/middleman-vegas/highlighter.rb
|
90
|
+
- lib/middleman-vegas/lexers/habitat_studio.rb
|
91
|
+
- lib/middleman-vegas/markdown_parser.rb
|
92
|
+
- lib/middleman-vegas/options_parser.rb
|
93
|
+
- lib/middleman-vegas/redcarpet_code_renderer.rb
|
94
|
+
- lib/middleman-vegas/version.rb
|
95
|
+
- middleman-vegas.gemspec
|
96
|
+
homepage: https://github.com/burtlo/middleman-vegas
|
97
|
+
licenses:
|
98
|
+
- MIT
|
99
|
+
metadata: {}
|
100
|
+
post_install_message:
|
101
|
+
rdoc_options: []
|
102
|
+
require_paths:
|
103
|
+
- lib
|
104
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
requirements: []
|
115
|
+
rubyforge_project:
|
116
|
+
rubygems_version: 2.6.11
|
117
|
+
signing_key:
|
118
|
+
specification_version: 4
|
119
|
+
summary: Add code highlighting to your middleman application.
|
120
|
+
test_files: []
|