Yessiest
1 year ago
2 changed files with 335 additions and 0 deletions
-
217markdown.rb
-
118test.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]/,"<br/>") |
||||
|
# Inline code (discord style) |
||||
|
.gsub(/(?<!\\)``(.*?[^\\])``/) { |
||||
|
code = Regexp.last_match[1] |
||||
|
"<code>#{code.gsub /[*`~_!\[]/,"\\\\\\0"}</code>" |
||||
|
} |
||||
|
# Inline code (Markdown style) |
||||
|
.gsub(/(?<!\\)`(.*?[^\\])`/) { |
||||
|
code = Regexp.last_match[1] |
||||
|
"<code>#{code.gsub /[*`~_!\[]/,"\\\\\\0"}</code>" |
||||
|
} |
||||
|
# Bold-italics |
||||
|
.gsub(/(?<!\\)\*\*\*(.*?[^\\])\*\*\*/,"<i><b>\\1</b></i>") |
||||
|
# Bold |
||||
|
.gsub(/(?<!\\)\*\*(.*?[^\\])\*\*/,"<b>\\1</b>") |
||||
|
# Italics |
||||
|
.gsub(/(?<!\\)\*(.*?[^\\])\*/,"<i>\\1</i>") |
||||
|
# Strikethrough |
||||
|
.gsub(/(?<!\\)~~(.*?[^\\])~~/,"<s>\\1</s>") |
||||
|
# Underline |
||||
|
.gsub(/(?<!\\)__(.*?[^\\])__/,"<span style=\"text-decoration: underline\">\\1</span>") |
||||
|
# Image |
||||
|
.gsub(/(?<!\\)!\[(.*)\]\((.*)\)/,"<img src=\"\\2\" alt=\"\\1\" />") |
||||
|
# Link |
||||
|
.gsub(/(?<!\\)\[(.*)\]\((.*)\)/,"<a href=\"\\2\">\\1</a>") |
||||
|
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(/^(?<!\\)(\#{1,4})([^\n\r]*)/) { |
||||
|
level,content = Regexp.last_match[1..2] |
||||
|
"<h#{level.length}>"+content+"</h#{level.length}>" |
||||
|
}.gsub(/^\-{3,}/,"<hr>") |
||||
|
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 |
||||
|
"<pre><code>#{code.gsub /[|#*`~_!\[]/,"\\\\\\0"}</code></pre>" |
||||
|
} |
||||
|
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] = "<blockquote>\n"+@lines[r.begin] |
||||
|
@lines[r.end] = @lines[r.end]+"\n</blockquote>" |
||||
|
@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? "#") ? " <th>"+x.sub(/^#\s+/,"")+"</th>" : " <td>"+x+"</td>"}).prepend(" <tr>").append(" </tr>") |
||||
|
end).flatten.prepend("<table>").append("</table>") |
||||
|
} |
||||
|
@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 |
||||
|
|
||||
|
|
@ -0,0 +1,118 @@ |
|||||
|
require_relative "markdown" |
||||
|
puts Markdown::LinearTagTranslator.new(<<CODE |
||||
|
*Italics* |
||||
|
**Bold** |
||||
|
***Bolitalics*** |
||||
|
__underline__ |
||||
|
__underline plus ***bolitalics***__ |
||||
|
___invalid underline___ |
||||
|
~~strikethrough ~~ |
||||
|
`code that ignores ***all*** __Markdown__ [tags](https://nevergonnagiveyouup)` |
||||
|
me: google en passant |
||||
|
them: [holy hell!](https://google.com/q?=en+passant) |
||||
|
CODE |
||||
|
).to_html |
||||
|
puts Markdown::LeftmostTagTranslator.new(<<CODE |
||||
|
# Header v1 |
||||
|
## Header v2 |
||||
|
### Header v3 |
||||
|
#### Header v4 |
||||
|
##### Invalid header |
||||
|
#### Not a header |
||||
|
*** Also #### Not a header *** |
||||
|
CODE |
||||
|
).to_html |
||||
|
puts Markdown::QuoteTranslator.new(<<CODE |
||||
|
> 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(<<CODE |
||||
|
```markdown |
||||
|
shmarkshmark |
||||
|
# pee pee |
||||
|
# piss |
||||
|
**ass** |
||||
|
__cock__ |
||||
|
cock__ |
||||
|
piss__ |
||||
|
`shmark shmark` |
||||
|
``` |
||||
|
CODE |
||||
|
).to_html |
||||
|
|
||||
|
test = (Markdown::CodeBlockTranslator.new(<<TEXT |
||||
|
# Markdown garbage gallery |
||||
|
## Header level 2 |
||||
|
### Header level 3 |
||||
|
#### Header level 4 |
||||
|
__[Underlined Link](https://google.com)__ |
||||
|
__**unreal shitworks**__ |
||||
|
|
||||
|
split |
||||
|
--- |
||||
|
|
||||
|
![Fucking image idk](https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Ftse3.explicit.bing.net%2Fth%3Fid%3DOIP.qX1HmpFNHyaTfXv-SLnAJgHaDD%26pid%3DApi&f=1&ipt=dc0e92fdd701395eda76714338060dcf91c7ff9e228f108d8af6e1ba3decd1c2&ipo=images) |
||||
|
> 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 |
Write
Preview
Loading…
Cancel
Save
Reference in new issue