diff --git a/markdown.rb b/markdown.rb new file mode 100644 index 0000000..cb3d380 --- /dev/null +++ b/markdown.rb @@ -0,0 +1,217 @@ +## Filter-based Markdown translator. +# +module Markdown + ## Superclass that defines behaviour of all translators + # @abstract Don't use directly - it only defins the ability to chain translators + class AbstractTranslator + attr_accessor :input + attr_accessor :output + def initialize() + @chain = [] + end + def +(nextTranslator) + @chain.append nextTranslator + return self + end + def to_html + output = @output + @chain.each { |x| + x = x.new(output) if x.class == Class + x.to_html + output = x.output + } + return output + end + end + module_function + def html_highlighter; @html_highlighter end + def html_highlighter= v; @html_highlighter = v end + ## Translator for linear tags in Markdown. + # A linear tag is any tag that starts anywhere on the line, and closes on the same exact line. + class LinearTagTranslator < AbstractTranslator + def initialize(text) + @input = text + @output = text + super() + end + def to_html + @output = @input + # Newline + .sub(/\s{2}[\n\r]/,"
") + # Inline code (discord style) + .gsub(/(?#{code.gsub /[*`~_!\[]/,"\\\\\\0"}" + } + # Inline code (Markdown style) + .gsub(/(?#{code.gsub /[*`~_!\[]/,"\\\\\\0"}" + } + # Bold-italics + .gsub(/(?\\1") + # Bold + .gsub(/(?\\1") + # Italics + .gsub(/(?\\1") + # Strikethrough + .gsub(/(?\\1") + # Underline + .gsub(/(?\\1") + # Image + .gsub(/(?") + # Link + .gsub(/(?\\1") + super + end + end + ## Translator for linear leftmost tags. + # Leftmost linear tags open on the leftmost end of the string, and close once the line ends. These tags do not need to be explicitly closed. + class LeftmostTagTranslator < AbstractTranslator + def initialize(text) + @input = text + @output = text + super() + end + def to_html + # Headers + @output = @input.split("\n").map do |x| + x.gsub(/^(?"+content+"" + }.gsub(/^\-{3,}/,"
") + end.join("\n") + super + end + end + ## Translator for code blocks in markdown + # Code blocks can have syntax highlighting. This class implements an attribute for providing a syntax highlighter, one handler per requested output. + class CodeBlockTranslator < AbstractTranslator + def initialize(text) + @input = text + @output = text + super() + end + def to_html + @output = @input.gsub(/(?:\n|^)```([\w_-]*)([\s\S]+?)```/) { + language,code = Regexp.last_match[1..2] + code = Markdown::html_highlighter.call(language,code) if Markdown::html_highlighter + "
#{code.gsub /[|#*`~_!\[]/,"\\\\\\0"}
" + } + super() + end + end + ## Translator for quotes in Markdown. + # These deserve their own place in hell. As if the "yaml with triangle brackets instead of spaces" syntax wasn't horrible enough, each quote is its own markdown context. + class QuoteTranslator < AbstractTranslator + def initialize(text) + if text.is_a? Array then + @lines = text + elsif text.is_a? String then + @lines = text.split("\n") + end + @output = text + super() + end + def input= (v) + @lines = v.split("\n") + @output = v + end + def input + @lines.join("\n") + end + def to_html + stack = [] + range = [] + @lines.each_with_index { |x,index| + if x.match /^\s*> ?/ then + range[0] = index if not range[0] + range[1] = index + else + stack.append(range[0]..range[1]) if range[0] and range[1] + range = [] + end + } + stack.append(range[0]..range[1]) if range[0] and range[1] + stack.reverse.each { |r| + @lines[r.begin] = "
\n"+@lines[r.begin] + @lines[r.end] = @lines[r.end]+"\n
" + @lines[r] = @lines[r].map { |line| + line.sub /^(\s*)> ?/,"\\1 " + } + @lines[r] = QuoteTranslator.new(@lines[r]).to_html + } + @output = @lines.join("\n") + super + end + end + + ## Table parser + # translates tables from a format in markdown to an html table + class TableTranslator < AbstractTranslator + def initialize(text) + @input = text + @output = text + super() + end + def to_html + lines = @output.split("\n") + table_testline = -1 + table_start = -1 + table_column_count = 0 + tables = [] + cur_table = [] + lines.each_with_index { |line,index| + if (table_start != -1) and (line.match /^\s*\|([^\|]*\|){#{table_column_count-1}}$/) then + if (table_testline == -1) then + if (line.match /^\s*\|(\-*\|){#{table_column_count-1}}$/) then + table_testline = 1 + else + table_start = -1 + cur_table = [] + end + else + cur_table.push (line.split("|").filter_map { |x| x.strip if x.match /\S+/ }) + end + elsif (table_start != -1) then + obj = {table: cur_table, start: table_start, end: index} + tables.push(obj) + table_start = -1 + cur_table = [] + table_testline = -1 + table_column_count = 0 + end + if (table_start == -1) and (line.start_with? /\s*\|/ ) and (line.match /^\s*\|.*\|/) then + table_start = index + table_column_count = line.count "|" + cur_table.push (line.split("|").filter_map { |x| x.strip if x.match /\S+/ }) + end + } + if cur_table != [] then + obj = {table: cur_table, start:table_start, end: lines.count-1} + tables.push(obj) + end + tables.reverse.each { |x| + lines[x[:start]..x[:end]] = (x[:table].map do |a2d| + (a2d.map { |x| (x.start_with? "#") ? " "+x.sub(/^#\s+/,"")+"" : " "+x+""}).prepend(" ").append(" ") + end).flatten.prepend("").append("
") + } + @output = lines.join("\n") + super() + end + end + + # Backslash cleaner + # Cleans excessive backslashes after the translation + class BackslashTranslator < AbstractTranslator + def initialize(text) + @input = text + @output = text + end + def to_html + @output = @input.gsub(/\\(.)/,"\\1") + end + end +end + + diff --git a/test.rb b/test.rb new file mode 100644 index 0000000..48cbeb5 --- /dev/null +++ b/test.rb @@ -0,0 +1,118 @@ +require_relative "markdown" +puts Markdown::LinearTagTranslator.new(< Quote begins +> +> yea +> # header btw +> > nextlevel quote +> > more quote +> > those are quotes +> > yes +> > > third level quote +> > > yes +> > second level again +> > > third level again +> > second level oioioi +> > +> > > third +> > > +> > > +> > > +> +> +> +> fin +CODE + ).to_html + +puts Markdown::CodeBlockTranslator.new(< Here's a bunch of shit i guess lmao idk +```markdown +test +test +test +|1|2|3| +|-|-|-| +|a|b|c| + +| uneven rows | test | yes | +|-|-|-| +| sosiska | dinozavri | suda pihaem | +| sosiska 2 | vitalya 2 | brat 2 | +*** test *** +piss +cock +__cock__ +# hi +``` +> ok +> here i go pissing +> ***time to take a piss*** +> > pissing +> > "what the hell are you doing" +> > i'm taking a pieeees +> > "why areyou not jomping at me thats what yourshupposed to do +> > I might do it focking later +> > ok +> # bug +> __cum__ +__mashup__ + +| # sosiska | sosiska | suda pihaem | +|-|-|-| +| # 2 | chuvak ya ukral tvayu sardelku ))0)))0))))))) | __blya ((9((9((9)__ | +| # azazaz lalka sasI | test | test | +TEXT + )+Markdown::QuoteTranslator+Markdown::LeftmostTagTranslator+Markdown::LinearTagTranslator+Markdown::TableTranslator+Markdown::BackslashTranslator) + .to_html +write = File.new("/tmp/test.html","w") +write.write(test) +write.close