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