markdownplus 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 59b5fb6c0a410a172db5e2aeba391eac6653eaff
4
- data.tar.gz: 42154e62a00f5ec65919c512ad6398be9498fc64
3
+ metadata.gz: b122d954d5f9a9b2fa74b04626054c7d4bb3fdd4
4
+ data.tar.gz: 812679f9e80a65de77281ec0d5f53b95038022cd
5
5
  SHA512:
6
- metadata.gz: c25d9c37887e2d41a4a3a12796fcb19badf60a4615c0e437c3ad2f5d1d0db6710a55b94f524c2fe76494d8c062180290c752309a374f9135c7bf8749724b575b
7
- data.tar.gz: 97334f94d603ac07051958b58a074703a798485943f9a5b1dccc556517c02eb3301d3c7addf5405040e44c3b1be45353cd7b1af0dd4c36dd47fed65e03163a6b
6
+ metadata.gz: 6506836776c5df3516be8187fe47441a0f0170133596b498d72ccf8046f0106ee49d1722631a52c84ec0db6a7ac98b8b20b75b1d04116c0ce3ba00d292bf893f
7
+ data.tar.gz: 6977a66ef13badcee4bf28594346313c66b0d01c8f516daabed6b718d7a8382fcb587546d1366b6bf2fd3330f55bdedf1d22da0f73d1f3307dfbc01efb538d36
data/README.md CHANGED
@@ -1,6 +1,141 @@
1
1
  # Markdownplus
2
2
 
3
- Coming soon. This is a prerelease gem.
3
+ Markdownplus extends [Github Flavored Markdown](https://help.github.com/articles/github-flavored-markdown/) by bringing programmatic features to the language portion of the fenced code blocks. A Markdownplus document can be transformed into a valid Markdown document, or taken all the way to html.
4
+
5
+ ## Usage
6
+
7
+ ### Syntax
8
+
9
+ Markdownplus files look like normal [Github Flavored Markdown](https://help.github.com/articles/github-flavored-markdown/) files. However, the fenced code blocks have most functionality. You can still use
10
+
11
+ ```markdown
12
+ ```ruby
13
+ ```
14
+
15
+ or
16
+
17
+ ```markdown
18
+ ```json
19
+ ```
20
+
21
+ to add syntax hightlight, but you can also use a pipeline of functions to bring in other files, or perform more drastic formatting.
22
+
23
+ For instance, you could download a json file, format it, then highlight it as usual using:
24
+
25
+ ```
26
+ ```include('https://gist.githubusercontent.com/cpetersen/c6571117df132443ac81/raw/e5ac97e8e0665a0e4014ebc85ecef214763a7729/fake.json'),pretty_json()
27
+ ```
28
+
29
+ or, you could download a csv file and turn it into an html table:
30
+
31
+ ```
32
+ ```include('https://gist.githubusercontent.com/cpetersen/b5a473ddf0b796cd9502/raw/e140bdc32ff2f6a600e357c2575220c0312a88ee/fake.csv'),csv2html()
33
+ ```
34
+
35
+ ### Execution
36
+
37
+ Given a markdown plus file, you can get the resulting Markdown using the following:
38
+
39
+ ```ruby
40
+ require 'markdownplus'
41
+
42
+ parser = Markdownplus::Parser.parse(File.read("kitchensink.mdp")); nil
43
+ parser.execute; nil
44
+ puts parser.output_markdown; nil
45
+ ```
46
+
47
+ If the resulting HTML is what you're interested in, you can use ```html```:
48
+
49
+ ```ruby
50
+ require 'markdownplus'
51
+
52
+ parser = Markdownplus::Parser.parse(File.read("kitchensink.mdp")); nil
53
+ parser.execute; nil
54
+ puts parser.html; nil
55
+ ```
56
+
57
+ ## Function Pipeline
58
+
59
+ The function pipeline is the heart of Markdownplus.
60
+
61
+ ### Functions
62
+
63
+ A function looks like:
64
+
65
+ ```
66
+ function_name()
67
+ function_name(symbol_parameter)
68
+ function_name("string parameter", 'other string parameter')
69
+ function_name(mix, 'and', match, "parameters")
70
+ function_name(you, "may pass", nested_methods("also"))
71
+ ```
72
+
73
+ The first function in the pipeline gets the contents of the fenced code block as input. For instance:
74
+
75
+ ```
76
+ ```pretty_json()
77
+ {"a":1,"b":2,"c":3}
78
+ ```
79
+
80
+ would get `{"a":1,"b":2,"c":3}` as the input variable and produce the following:
81
+
82
+ ```json
83
+ {
84
+ "a": 1,
85
+ "b": 2,
86
+ "c": 3
87
+ }
88
+ ```
89
+
90
+ ### Pipeline
91
+
92
+ You create pipeline, you string multiple functions together with a comma:
93
+
94
+ ```
95
+ ```include("some url"), csv2html()
96
+ ```
97
+
98
+ In this case, the first function (include) gets the contents of the fenced code block as input. The second function (csv2html) gets the output of the first function as input.
99
+
100
+ The output of the last function in the pipeline is used as the content when generating the `output_markdown` and ultimately the resulting `html`
101
+
102
+ ## Built in functions:
103
+
104
+ ### include()
105
+
106
+ `include` takes a single parameter, a url. It downloads this url and outputs the result.
107
+
108
+ ### csv2html()
109
+
110
+ `csv2html` takes no parameters, but expects valid CSV as input. It creates an HTML table from the given CSV.
111
+
112
+ ### pretty_json()
113
+
114
+ `pretty_json` takes no parameters, but expects valid JSON as input. It formats the JSON nicely using Ruby's `JSON.pretty_generate` and outputs a fenced code block with the language specified as `json`.
115
+
116
+ ### set('variable_name')
117
+
118
+ Stores the input in a `variable_name` for use later in the page.
119
+
120
+ ### get('variable_name')
121
+
122
+ Outputs the content stored at a the `variable_name`.
123
+
124
+ ### empty()
125
+
126
+ Hides the output. Often used with `set()`
127
+
128
+ ### raw()
129
+
130
+ Output the content as raw html and skip any markdown formatting that may apply.
131
+
132
+ ### strip_whitespace()
133
+
134
+ Strips any leading or trailing whitespace from the `input` and outputs the result.
135
+
136
+ ## Extendable
137
+
138
+
4
139
 
5
140
  ## Installation
6
141
 
@@ -23,7 +158,6 @@ Or install it yourself as:
23
158
  Use a fenced code block:
24
159
 
25
160
  ```markdown
26
- ```include|csv
27
- https://gist.githubusercontent.com/cpetersen/b5a473ddf0b796cd9502/raw/e140bdc32ff2f6a600e357c2575220c0312a88ee/fake.csv
161
+ ```include('https://gist.githubusercontent.com/cpetersen/b5a473ddf0b796cd9502/raw/e140bdc32ff2f6a600e357c2575220c0312a88ee/fake.csv'),csv()
28
162
  ```
29
163
  ```
data/lib/markdownplus.rb CHANGED
@@ -1,5 +1,10 @@
1
1
  require "markdownplus/version"
2
+ require "markdownplus/github_renderer"
2
3
  require "markdownplus/parser"
4
+ require "markdownplus/literals"
5
+ require "markdownplus/directive_parser"
6
+ require "markdownplus/handler_registry"
7
+ require "markdownplus/handler"
3
8
 
4
9
  module Markdownplus
5
10
  # Your code goes here...
@@ -0,0 +1,17 @@
1
+ require 'treetop'
2
+
3
+ module Markdownplus
4
+ class DirectiveParser
5
+ def self.parse(data)
6
+ Treetop.load(File.expand_path("../directives", __FILE__))
7
+ @@parser ||= TransformationParser.new
8
+ tree = @@parser.parse(data)
9
+ # If the AST is nil then there was an error during parsing
10
+ # we need to report a simple error message to help the user
11
+ raise "Parse error at offset: #{@@parser.index}" if(tree.nil?)
12
+
13
+ return tree
14
+ end
15
+ end
16
+ end
17
+
@@ -0,0 +1,33 @@
1
+ grammar Transformation
2
+ rule transformations
3
+ ( function / "" ) (',' function)* <Markdownplus::Literals::TransformationLiteral>
4
+ end
5
+
6
+ rule expression
7
+ ( function / single_quote_string / double_quote_string / symbol / "" ) (',' expression)* <Markdownplus::Literals::ExpressionLiteral>
8
+ end
9
+
10
+ rule function
11
+ symbol parameters <Markdownplus::Literals::FunctionLiteral>
12
+ end
13
+
14
+ rule parameters
15
+ "(" expression ")" <Markdownplus::Literals::ParensLiteral>
16
+ end
17
+
18
+ rule single_quote_string
19
+ space? "'" [a-zA-Z0-9\-_@ \t:\/\.]+ "'" space? <Markdownplus::Literals::StringLiteral>
20
+ end
21
+
22
+ rule double_quote_string
23
+ space? '"' [a-zA-Z0-9\-_@ \t:\/\.]+ '"' space? <Markdownplus::Literals::StringLiteral>
24
+ end
25
+
26
+ rule symbol
27
+ space? [a-zA-Z0-9\-_]+ space? <Markdownplus::Literals::SymbolLiteral>
28
+ end
29
+
30
+ rule space
31
+ [\s]+
32
+ end
33
+ end
@@ -0,0 +1,19 @@
1
+ require 'pygments'
2
+ require 'redcarpet'
3
+
4
+ module Markdownplus
5
+ class GithubRenderer < Redcarpet::Render::HTML
6
+ # alias_method :existing_block_code, :block_code
7
+ def block_code(code, language)
8
+ if language == "raw"
9
+ code
10
+ else
11
+ begin
12
+ Pygments.highlight(code, lexer: language)
13
+ rescue
14
+ "<pre><code>#{code}</code></pre>"
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,120 @@
1
+ module Markdownplus
2
+ class Handler
3
+ def execute(input, parameters)
4
+ end
5
+ end
6
+
7
+ class IncludeHandler < Handler
8
+ def execute(input, parameters, variables, warnings, errors)
9
+ output = nil
10
+ warnings << "Include handler ignores input" if(input!=nil && !input.strip.empty?)
11
+ if parameters==nil
12
+ errors << "No url given"
13
+ elsif parameters.count == 0
14
+ errors << "No url given"
15
+ else
16
+ begin
17
+ output = IncludeHandler.cached(parameters.first.to_s)
18
+ rescue => e
19
+ errors << "Error opening [#{parameters.first}] [#{e.message}]"
20
+ end
21
+ end
22
+ output
23
+ end
24
+
25
+ @@cache = {}
26
+ def self.cached(url)
27
+ return @@cache[url] if @@cache[url]
28
+ @@cache[url] = open(url).read
29
+ @@cache[url]
30
+ end
31
+ end
32
+ HandlerRegistry.register("include", IncludeHandler)
33
+
34
+ class Csv2HtmlHandler < Handler
35
+ def execute(input, parameters, variables, warnings, errors)
36
+ output = "<table class='table table-striped'>"
37
+ row_num = 0
38
+ CSV.parse(input) do |row|
39
+ if row_num == 0
40
+ output += "<thead><tr>#{row.collect { |c| "<th>#{c}</th>"}.join}</tr></thead>\n<tbody>\n"
41
+ else
42
+ output += "<tr>#{row.collect { |c| "<td>#{c}</td>"}.join}</tr>\n"
43
+ end
44
+ row_num += 1
45
+ end
46
+ output += "</tbody></table>"
47
+ output
48
+ end
49
+ end
50
+ HandlerRegistry.register("csv2html", Csv2HtmlHandler)
51
+
52
+ class PrettyJsonHandler < Handler
53
+ def execute(input, parameters, variables, warnings, errors)
54
+ begin
55
+ obj = JSON.parse(input)
56
+ output = JSON.pretty_generate(obj)
57
+ rescue => e
58
+ output = input
59
+ errors << "Invalid json"
60
+ end
61
+ "```json\n#{output}\n```"
62
+ end
63
+ end
64
+ HandlerRegistry.register("pretty_json", PrettyJsonHandler)
65
+
66
+ class StripWhitespaceHandler < Handler
67
+ def execute(input, parameters, variables, warnings, errors)
68
+ input.gsub(/\s*\n\s*/,"\n")
69
+ end
70
+ end
71
+ HandlerRegistry.register("strip_whitespace", StripWhitespaceHandler)
72
+
73
+ class RawHandler < Handler
74
+ def execute(input, parameters, variables, warnings, errors)
75
+ "```raw\n#{input}\n```"
76
+ end
77
+ end
78
+ HandlerRegistry.register("raw", RawHandler)
79
+
80
+ class EmptyHandler < Handler
81
+ def execute(input, parameters, variables, warnings, errors)
82
+ ""
83
+ end
84
+ end
85
+ HandlerRegistry.register("empty", EmptyHandler)
86
+
87
+ ### START VARIABLES ###
88
+ class SetHandler < Handler
89
+ def execute(input, parameters, variables, warnings, errors)
90
+ if parameters==nil
91
+ errors << "No variable name given"
92
+ elsif parameters.count == 0
93
+ errors << "No variable name given"
94
+ else
95
+ warnings << "More than one variable name given [#{parameters.inspect}]" if parameters.count > 1
96
+ variables[parameters.first.to_s] = input
97
+ end
98
+ input
99
+ end
100
+ end
101
+
102
+ class GetHandler < Handler
103
+ def execute(input, parameters, variables, warnings, errors)
104
+ output = input
105
+ if parameters==nil
106
+ errors << "No variable name given"
107
+ elsif parameters.count == 0
108
+ errors << "No variable name given"
109
+ else
110
+ warnings << "More than one variable name given [#{parameters.inspect}]" if parameters.count > 1
111
+ output = variables[parameters.first.to_s]
112
+ end
113
+ output
114
+ end
115
+ end
116
+
117
+ HandlerRegistry.register("set", SetHandler)
118
+ HandlerRegistry.register("get", GetHandler)
119
+ #### END VARIABLES ####
120
+ end
@@ -0,0 +1,17 @@
1
+ module Markdownplus
2
+ class HandlerRegistry
3
+ @@registry = {}
4
+
5
+ def self.handler_instance(name)
6
+ handler_class(name).new if handler_class(name)
7
+ end
8
+
9
+ def self.handler_class(name)
10
+ @@registry[name]
11
+ end
12
+
13
+ def self.register(name, handler)
14
+ @@registry[name] = handler
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,101 @@
1
+ require 'treetop'
2
+
3
+ module Markdownplus
4
+ module Literals
5
+ class ExpressionLiteral < Treetop::Runtime::SyntaxNode
6
+ def functions
7
+ _functions(self.elements).flatten.compact
8
+ end
9
+
10
+ def _functions(elements)
11
+ return unless elements
12
+ results = elements.select { |e| e.class==Markdownplus::Literals::FunctionLiteral }
13
+ elements.each do |element|
14
+ if [Treetop::Runtime::SyntaxNode, Markdownplus::Literals::ExpressionLiteral, Markdownplus::Literals::TransformationLiteral].include?(element.class)
15
+ results << _functions(element.elements)
16
+ end
17
+ end
18
+ results
19
+ end
20
+ def symbols
21
+ self.elements.select { |e| e.class==SymbolLiteral }
22
+ end
23
+ def parens
24
+ self.elements.select { |e| e.class==ParensLiteral }
25
+ end
26
+ end
27
+
28
+ class TransformationLiteral < ExpressionLiteral
29
+ #Specific subclass, the root should only match this
30
+ end
31
+
32
+ class FunctionLiteral < ExpressionLiteral
33
+ def function_name
34
+ self.symbols[0].text_value.strip
35
+ end
36
+
37
+ def function_parameters
38
+ self.parens.first.function_parameters
39
+ end
40
+
41
+ def function_parameter_values(input, variables, warnings, errors)
42
+ self.parens.first.function_parameters.collect { |fp| fp.value(input, variables, warnings, errors) }
43
+ end
44
+
45
+ def execute(input, variables, warnings, errors)
46
+ handler = HandlerRegistry.handler_instance(self.function_name)
47
+ if handler
48
+ output = handler.execute(input, self.function_parameter_values(nil, variables, warnings, errors), variables, warnings, errors)
49
+ else
50
+ errors << "No handler defined for [#{self.function_name}]"
51
+ end
52
+ output
53
+ end
54
+
55
+ def value(input, variables, warnings, errors)
56
+ execute(input, variables, warnings, errors)
57
+ end
58
+
59
+ end
60
+
61
+ class ParensLiteral < ExpressionLiteral
62
+ def function_parameters
63
+ self.find_parameters(self.elements)
64
+ end
65
+
66
+ def find_parameters(elements, params=[])
67
+ return params unless elements
68
+ elements.each do |element|
69
+ if [StringLiteral, SymbolLiteral, FunctionLiteral].include?(element.class)
70
+ params << element
71
+ else
72
+ find_parameters(element.elements, params)
73
+ end
74
+ end
75
+ return params
76
+ end
77
+ end
78
+
79
+ class StringLiteral < ExpressionLiteral
80
+ def to_s
81
+ v = self.text_value.strip
82
+ v[1..v.length-2]
83
+ end
84
+
85
+ def value(input, variables, warnings, errors)
86
+ to_s
87
+ end
88
+ end
89
+
90
+ class SymbolLiteral < ExpressionLiteral
91
+ def to_s
92
+ self.text_value.strip
93
+ end
94
+
95
+ def value(input, variables, warnings, errors)
96
+ to_s
97
+ end
98
+ end
99
+ end
100
+ end
101
+