#!/usr/bin/env python
# -*- coding: utf-8 -*-
from re import compile as RECompile
from re import Pattern as REPattern
from re import Match as REMatch
from re import MULTILINE as RE_MULTILINE
from re import IGNORECASE as RE_IGNORE_CASE
from typing import Callable, Optional
from os.path import exists as path_exists
from os.path import dirname as directory_name
from base64 import b64encode as base64_encode
from json import dumps as json_encode
class WMarkDown:
# Languages
HTML:int = 1 << 0
# Analyse modes
RAW:int = 0
SUBITEM:int = 1 << 0
LINKED:int = 1 << 1
html_special_characters:dict[str, str] = {
"<" : "lt",
">" : "gt",
"&" : "amp"
}
quote_special:dict[str, str] = {
"?" : "ask",
"!" : "warning",
">" : "answer",
"Q" : "comment",
"#" : "note"
}
item_mark:tuple[str, str, REPattern] = ("###@&&_", "_&&@###", RECompile(r'\#{3}\@\&{2}_([0-9]+)_\&{2}\@\#{3}'))
re_block_html_pattern:str = r'^[ \t]*<\!\-{2}[ \t]*\[{2}[\t ]*(wmd|wmarkdown)[ \t]*\]{2}[\t ]*\-{2}>'
re_block_html:REPattern = RECompile(re_block_html_pattern, RE_MULTILINE)
re_block_mark:REPattern = RECompile(r'^[ \t]*((" ?){3}|(\' ?){3}|(` ?){3})[ \t]*(wmd|wmarkdown)|' + re_block_html_pattern, RE_MULTILINE)
re_new_lines:REPattern = RECompile(r'[\r\n]+')
re_new_line:REPattern = RECompile(r'\n|\r\n|\r')
re_started_white_spaces:REPattern = RECompile(r'^[ \t]*')
re_lines:REPattern = RECompile(r'^[^\r\n]+', RE_MULTILINE)
re_list_line:REPattern = RECompile(r'^([ \t]*)(([\-\*#\+]+)|([0-9]+|[a-z]+)\.)?(.*)$', RE_IGNORE_CASE | RE_MULTILINE)
re_table_line:REPattern = RECompile(r'^\|([^\r\n"\']+|"([^"\\\\]+|\\\\(.|[\r\n])|[\r\n]+)*"|\'([^\'\\\\]+|\\\\(.|[\r\n])|[\r\n]+)*\')*[\r\n]*', RE_MULTILINE)
re_table_item:REPattern = RECompile(r'(\|+)(([^\|\'\"]+|"([^"\\\\]+|\\\\(.|[\r\n])|[\r\n]+)*"|\'([^\'\\\\]+|\\\\(.|[\r\n])|[\r\n]+)*\')*)')
re_options:REPattern = RECompile(r'`{3}[ \t]*(wmd|wmarkdown)\-options[ \t]*(\n|\r\n|\r)(([^\r\n`]+|[\r\n]+|`{1,2}[^`])*)`{3}')
re_option:REPattern = RECompile(r'^([^=\r\n]+)=([^\r\n]*)$', RE_MULTILINE)
re_extension:REPattern = RECompile(r'(([^\.\/\\\\]+)\.)?([^\.\/\\\\]+)$')
re_characters_no_id:REPattern = RECompile(r'[^a-z0-9]+', RE_IGNORE_CASE)
re_phone_number:REPattern = RECompile(r'^\+?[0-9 ]{5,22}$')
re_email_address:REPattern = RECompile(r'^[a-z\.0-9_\-]+@[a-z\.0-9_\-]+\.[a-z0-9]+$')
re_class_attribute:REPattern = RECompile(r'(?]+)>|("[^"]*"|\'[^\']*\')')
re_start_with_white_spaces:REPattern = RECompile(r'^[ \t]', RE_MULTILINE)
re_domain:REPattern = RECompile(r'^[^\:]+\:\/{2}[^\/]+')
re_protocol:REPattern = RECompile(r'^([a-z0-9_\-]+)\:', RE_IGNORE_CASE)
def __init__(self) -> None:
self.modules:dict[str, tuple[int, REPattern]] = {
"special_characters" : (WMarkDown.SUBITEM, RECompile(r'\\([\(\{\[\*\\])')),
"title" : (WMarkDown.RAW, RECompile(r'^((#{1,6})[\t ]*([^\r\n]+)|(={1,6})[\t ]*([^\r\n=]+)={1,6})(\n|\r\n|\r){2,}', RE_MULTILINE)),
"list" : (WMarkDown.RAW, RECompile(r'^([\-\*#\+]+|([0-9]+|[a-z]+)\.)[^\r\n]*(\n|\r\n|\r)([\t ]*(([\-\*#\+]+|([0-9]+|[a-z]+)\.)[^\r\n]*)(\n|\r\n|\r)?)*', RE_IGNORE_CASE | RE_MULTILINE)),
"code_block" : (WMarkDown.RAW, RECompile(r'^("{3}([^\r\n]*)(((?!"{3})(.|[\r\n]))+)"{3}|" " "([^\r\n]*)(((?!" " ")(.|[\r\n]))+)" " "|\'{3}([^\r\n]*)(((?!\'{3})(.|[\r\n]))+)\'{3}|\' \' \'([^\r\n]*)(((?!\' \' \')(.|[\r\n]))+)\' \' \'|`{3}([^\r\n]*)(((?!`{3})(.|[\r\n]))+)`{3}|` ` `([^\r\n]*)(((?!` ` `)(.|[\r\n]))+)` ` `)', RE_MULTILINE)),
"table" : (WMarkDown.RAW, RECompile(r'^\[\|([^\r\n]*)[\r\n]+((^\|([^\]]([^\r\n"\']|"([^"\\\\]+|\\\\(.|[\r\n])|[\r\n]+)*"|\'([^\'\\\\]+|\\\\(.|[\r\n])|[\r\n]+)*\')*)?[\r\n]+)*)^\|\]', RE_MULTILINE)),
"quote" : (WMarkDown.RAW, RECompile(r'^>[ \t]*(\[\![\t ]*([^\] \t]+)([ \t]+([^\]]+))?\])?(([^\r\n]+(\r\n|[\r\n])?)*)', RE_MULTILINE)),
"include" : (WMarkDown.RAW, RECompile(r'^\[{2}include[ \t]+([^\]]+)\]{2}', RE_MULTILINE)),
"media" : (WMarkDown.SUBITEM | WMarkDown.LINKED, RECompile(r'\({2}\!(image|icon|video|audio|sound|picture)[ \t]+("(([^"]+|[\r\n]+))"|\'(([^\']+|[\r\n]+))\'|([^ \t\)]+))([ \t]*|[ \t]+("(([^\\\\"]+|\\\\.|[\r\n]+)*)"|\'(([^\\\\\']+|\\\\.|[\r\n]+)*)\'|([^\)]+)))\){2}', RE_IGNORE_CASE)),
"code_doc" : (WMarkDown.RAW, RECompile(r'^\[{2}@(([^\]]+|\][^\]])+)\]{2}', RE_MULTILINE)),
"presentation_links" : (WMarkDown.RAW, RECompile(r'^\[{2}"{3}(([^"]+|[\r\n]+|"{1,2}[^"])*)"{3}\]{2}', RE_MULTILINE)),
"paragraph" : (WMarkDown.RAW, RECompile(r'^(([^\r\n]+(\n|\r\n|\r)?)+)', RE_MULTILINE)),
# "bold_italic" : (WMarkDown.SUBITEM | WMarkDown.LINKED, RECompile(r'\*{3}(([^\*]+|\*{1,2}[^\*]|[\r\n]+|\\\*)*)\*{3}')),
# "bold_italic" : (WMarkDown.SUBITEM | WMarkDown.LINKED, RECompile(r'(?]+|[a-z\.0-9_\-]+@[a-z\.0-9_\-]+\.[a-z0-9]+)', RE_IGNORE_CASE))
}
self.__ids_cache:tuple[str] = tuple()
def __id(self, string:str) -> str:
id:str = WMarkDown.re_characters_no_id.sub(r'-', string).lower()
if id[0] == '-':
id = id[1:]
if id[-1] == '-':
id = id[:-1]
if id in self.__ids_cache:
i:int = 2
while id + "-" + str(i) in self.__ids_cache:
i += 1
id += "-" + str(i)
self.__ids_cache += (id,)
return id
def reset_ids(self):
self.__ids_cache = tuple()
def module_title(self, matches:REMatch, language:Optional[int] = HTML, path:Optional[str] = None) -> str|None:
if language & WMarkDown.HTML:
level:str = str(len(matches.group(2) or matches.group(4)))
content:str = (matches.group(3) or matches.group(5)).strip()
id:str = self.__id(content)
return ('' +
'' + self.analyse(content, language, WMarkDown.SUBITEM, path) + '' +
'' +
'' +
'' +
'')
return None
def module_paragraph(self, matches:REMatch, language:Optional[int] = HTML, path:Optional[str] = None) -> str|None:
if language & WMarkDown.HTML:
return '
' + self.analyse(matches.group(0).strip(), language, WMarkDown.SUBITEM, path) + '
'
return None
def module_bold_italic(self, matches:REMatch, language:Optional[int] = HTML, path:Optional[str] = None) -> str|None:
if language & WMarkDown.HTML:
return '' + self.analyse(matches.group(1), language, WMarkDown.SUBITEM, path) + ''
return None
def module_bold(self, matches:REMatch, language:Optional[int] = HTML, path:Optional[str] = None) -> str|None:
if language & WMarkDown.HTML:
return '' + self.analyse(matches.group(1), language, WMarkDown.SUBITEM, path) + ''
return None
def module_italic(self, matches:REMatch, language:Optional[int] = HTML, path:Optional[str] = None) -> str|None:
if language & WMarkDown.HTML:
return '' + self.analyse(matches.group(1), language, WMarkDown.SUBITEM, path) + ''
return None
def module_strike(self, matches:REMatch, language:Optional[int] = HTML, path:Optional[str] = None) -> str|None:
if language & WMarkDown.HTML:
return '' + self.analyse(matches.group(1), language, WMarkDown.SUBITEM, path) + ''
return None
def module_underline(self, matches:REMatch, language:Optional[int] = HTML, path:Optional[str] = None) -> str|None:
if language & WMarkDown.HTML:
return '' + self.analyse(matches.group(1), language, WMarkDown.SUBITEM, path) + ''
return None
def module_code_item(self, matches:REMatch, language:Optional[int] = HTML, path:Optional[str] = None) -> str|None:
if language & WMarkDown.HTML:
return '' + self.analyse(matches.group(1), language, WMarkDown.SUBITEM, path) + '
'
return None
@staticmethod
def __check_html_module(_type:str, matches:REMatch) -> str:
is_radio:bool = _type == "radio"
return ('')
@classmethod
def module_checkbox(self, matches:REMatch, language:Optional[int] = HTML, path:Optional[str] = None) -> str|None:
if language & WMarkDown.HTML:
return WMarkDown.__check_html_module("checkbox", matches)
return None
@classmethod
def module_radio(self, matches:REMatch, language:Optional[int] = HTML, path:Optional[str] = None) -> str|None:
if language & WMarkDown.HTML:
return WMarkDown.__check_html_module("radio", matches)
return None
@classmethod
def module_tick(self, matches:REMatch, language:Optional[int] = HTML, path:Optional[str] = None) -> str|None:
if language & WMarkDown.HTML:
return WMarkDown.__check_html_module("tick", matches)
return None
@staticmethod
def __list_deployed(level:list[int, str, str|None], last_mark:str|None, l:int) -> str:
_type:str = last_mark if l and last_mark else level[2]
deployable:bool = l and _type and _type in "+-"
deployed:str = (
"null" if not deployable else
"false" if _type == '-' else
"true")
return ' data-deployed="' + deployed + '"' + (' data-list-unprocessed="true"' if deployable else '')
@classmethod
def __list_start(self, unordered:bool, _type:str) -> str:
if not unordered:
_type = _type[:-1]
if WMarkDown.re_integer.search(_type):
return ' start="' + _type + '"'
return ''
def module_list(self, matches:REMatch, language:Optional[int] = HTML, path:Optional[str] = None) -> str|None:
if language & WMarkDown.HTML:
html:str = ''
lines:list[str] = WMarkDown.re_new_line.split(matches.group(0))
levels:list[list[int, str, str|None]] = [[0, 'u', None]]
l:int = 0
line:str
last_mark:str|None = None
subtype:str|None = None
for line in lines:
matches:REMatch = WMarkDown.re_list_line.search(line)
if not matches:
continue
separator_by_marks:int = len(matches.group(3) or "")
separator:int = separator_by_marks if separator_by_marks > 1 else len(matches.group(1))
_type:str = matches.group(2)
key:str = matches.group(4)
string:str = self.analyse(matches.group(5), language, WMarkDown.SUBITEM, path).strip()
unordered:bool = _type and _type[-1] in ('-', '*', '+')
subtype = _type[-1] if _type else None
if _type:
if levels[l][0] == separator:
levels[l][1] = 'u' if unordered else 'o'
levels[l][2] = subtype
html += ('' if html else '<' + str(levels[l][1]) + 'l class="wmd-list"' + WMarkDown.__list_start(unordered, _type) + WMarkDown.__list_deployed(levels[l - 1], last_mark, l) + (
' type="' + key[0] + '"' if key and not unordered and key[0] in ('a', 'A', 'i', 'I') else
'') + '>') + '' + string
elif levels[l][0] < separator:
l += 1
levels += [[separator, "u" if unordered else "o", subtype]]
html += '<' + levels[l][1] + 'l class="wmd-list"' + WMarkDown.__list_start(unordered, _type) + WMarkDown.__list_deployed(levels[l - 1], last_mark, l) + (
' type="' + key[0] + '"' if key and not unordered and key[0] in ('a', 'A', 'i', 'I') else
'') + '>' + string
else:
while levels[l][0] > separator:
html += '' + levels.pop(l)[1] + 'l>'
l -= 1
if not l:
break
html += '' + string
else:
html += ' ' + string
last_mark = subtype
while len(levels):
html += '' + levels.pop(len(levels) - 1)[1] + 'l>'
return html
return None
@classmethod
def set_class(self, html:str, _class:str|list|tuple) -> str:
if not isinstance(_class, (list, tuple)):
_class = (_class,)
if 'class="' not in html:
html = (' ' if html else '') + 'class="' + " ".join(_class) + '"'
def callback(matches:REMatch):
attribute:str
classes_str:str
closer:str
classes:list[str]
class_key:str
attribute, classes_str, closer = matches.groups()
classes = WMarkDown.re_white_spaces.split(classes_str)
return attribute + classes_str + (' ' if classes_str else '') + " ".join([
class_key for class_key in _class if class_key not in classes
]) + closer
return WMarkDown.re_class_attribute.sub(callback, html)
def module_table(self, matches:REMatch, language:Optional[int] = HTML, path:Optional[str] = None) -> str|None:
if language & WMarkDown.HTML:
html:dict[str, str] = {
"thead" : '',
"tbody" : '',
"tfoot" : ''
}
attributes:str = (matches.group(1) or "").strip()
data:str = matches.group(2).strip()
while True:
line_matches:REMatch = WMarkDown.re_table_line.search(data)
if not line_matches:
break
line:str = line_matches.group(0)
cell_type:str = 'h' if line[1] in ('^', '_', '=') else 'd'
tags:tuple[str] = (
("thead",) if line[1] == '^' else
("tfoot",) if line[1] == '_' else
("thead", "tfoot") if line[1] == '=' else
("tbody",))
tag:str
row:str = ''
if cell_type == 'h':
line = line[0] + line[2:]
while True:
cell_matches:REMatch = WMarkDown.re_table_item.search(line)
if not cell_matches:
break
column_span_i:int = len(cell_matches.group(1))
column_span:str = ' colspan="' + str(column_span_i) + '"' if column_span_i > 1 else ''
content:str = (cell_matches.group(2) or "").strip()
row += '' + self.analyse(
content[1:-1] if content and content[0] + content[-1] in ("''", '""') else content,
WMarkDown.HTML,
WMarkDown.SUBITEM,
path
) + ''
line = line[cell_matches.span()[1]:]
row += '
'
for i, tag in enumerate(tags):
if i:
html[tag] = row + html[tag]
else:
html[tag] += row
data = data[line_matches.span()[1]:]
return ('' +
'
' + "".join([
'<' + tag + '>' + content + '' + tag + '>' if content else '' for tag, content in html.items()
]) + '
' +
'
')
return None
@classmethod
def module_exclude(self, matches:REMatch, language:Optional[int] = HTML, path:Optional[str] = None) -> str|None:
if language & WMarkDown.HTML:
return '' + (matches.group(1) or "") + ''
return None
def module_link(self, matches:REMatch, language:Optional[int] = HTML, path:Optional[str] = None) -> str|None:
if language & WMarkDown.HTML:
raw_url:str = (matches.group(4) or matches.group(11) or matches.group(14) or "").strip()
url = (
"tel:" + raw_url.replace(" ", "") if WMarkDown.re_phone_number.search(raw_url) else
"mailto:" + raw_url if WMarkDown.re_email_address.search(raw_url) else
raw_url)
text:str = matches.group(2) or matches.group(13) or raw_url
protocol_matches:REMatch|None = WMarkDown.re_protocol.search(url)
return '' + self.analyse(text.strip(), language, WMarkDown.SUBITEM | self.LINKED, path) + ''
return None
def module_quote(self, matches:REMatch, language:Optional[int] = HTML, path:Optional[str] = None) -> str|None:
if language & WMarkDown.HTML:
_type:str|None = matches.group(2)
type_text:str|None = matches.group(4)
if _type in WMarkDown.quote_special:
_type = WMarkDown.quote_special[_type]
return ('' +
(('' + ((
'' +
'' + _type[1:] + ''
) if _type[0] == "@" else (
'' +
('' + type_text + '' if type_text else '')
)) + '
') if _type else '') +
'' + self.analyse(matches.group(5), language, WMarkDown.SUBITEM, path) + '
' +
'
')
return None
@classmethod
def code_block_data(self, key:str, value:str|int, language:Optional[int] = HTML, path:Optional[str] = None) -> str|None:
if language & WMarkDown.HTML:
return ('' +
'' +
'' + key + '' +
'' + str(value) + '' +
'')
return None
@classmethod
def filter_html_special_characters(self, string:str) -> str:
l:int = len(WMarkDown.html_special_characters)
_:int
index:list[int] = [-1 for _ in range(l)]
response:str = ''
characters:tuple[str] = tuple(WMarkDown.html_special_characters.keys())
while True:
j:int
character:str
i:int|None = None
for j, character in enumerate(characters):
if index[j] == None:
continue
if index[j] < 0:
try:
index[j] = string.index(character)
except:
index[j] = None
continue
if i == None or index[i] > index[j]:
i = j
if i == None:
response += string
break
length:int = index[i]
response += string[:length] + '&' + WMarkDown.html_special_characters[characters[i]] + ';'
string = string[length + 1:]
for j in range(l):
if index[j] != None:
index[j] -= length + 1
return response
@classmethod
def module_code_block(self, matches:REMatch, language:Optional[int] = HTML, path:Optional[str] = None) -> str|None:
if language & WMarkDown.HTML:
_type:str
content:str
i:int
for i in range(6):
j:int = 2 + i * 4
_type = matches.group(j)
content = matches.group(j + 1)
if _type or content:
_type = (_type or "unamed").strip().lower()
content = (content or "")
break
if _type:
lines:list[str] = WMarkDown.re_new_line.split(content)[2:]
_:str
return (''
'
' +
' ' +
WMarkDown.code_block_data("type", _type, language) +
WMarkDown.code_block_data("characters", len(content), language) +
WMarkDown.code_block_data("lines", len(lines), language) +
'
' +
'
' +
'
' + ''.join(['' for _ in lines]) + '
' +
'
' + WMarkDown.filter_html_special_characters(content) + '
' +
'
' +
'
')
return 'UNKNOWN_BLOCK'
return None
def module_special_characters(self, matches:REMatch, language:Optional[int] = HTML, path:Optional[str] = None) -> str|None:
if language & WMarkDown.HTML:
return matches.group(1)
return None
@staticmethod
def load_file(path:str) -> str|None:
if path_exists(path):
try:
with open(path, "r") as opened:
return opened.read()
except:
pass
return None
def module_include(self, matches:REMatch, language:Optional[int] = HTML, path:Optional[str] = None) -> str|None:
if language & WMarkDown.HTML:
relative_path:str = matches.group(1)
new_path:str = path + relative_path
extension:str = WMarkDown.re_extension.search(new_path).groups()[1:]
content:str|None = WMarkDown.load_file(new_path)
directory:str = directory_name(new_path) + "/"
results:str|None = (
None if content == None else
self.process(content, language, directory)[0] if ".".join(extension) == "w.md" else
self.analyse(content, language, WMarkDown.RAW, directory) if extension[-1] == "md" else
content)
return ('' +
'' +
(results or "") +
'')
return None
@staticmethod
def build_html_image(links:list[str]|tuple[str], _type:Optional[str] = "image", title:Optional[str] = None) -> str:
attributes:str = ' title="' + title + '" alt="' + title + '"' if title else ''
return ('' +
'' +
'' +
'' +
('' + title + '' if title else '') +
'')
def module_media(self, matches:REMatch, language:Optional[int] = HTML, path:Optional[str] = None) -> str|None:
if language & WMarkDown.HTML:
_type:str = matches.group(1).lower()
links:list[str] = WMarkDown.re_white_spaces.split(matches.group(3) or matches.group(5) or matches.group(7))
text:str = (matches.group(10) or matches.group(12) or matches.group(14) or '').strip()
if _type in ("image", "picture", "icon"):
return WMarkDown.build_html_image(links, _type, text)
if _type in ("video",):
if "youtube.com" in links[0] or "youtu.be" in links[0]:
_type = "youtube"
elif "vimeo.com" in links[0]:
_type = "vimeo"
return ('' +
'' +
(
"YOUTUBE" if _type == "youtube" else
"VIMEO" if _type == "vimeo" else
'') +
'')
if _type in ("sound", "audio"):
if "soundcloud.com" in links[0]:
_type = "soundcloud"
return ('' +
'' +
(
"SOUNDCLOUD" if _type == "soundcloud" else
'') +
'')
return None
return None
@classmethod
def mark_replace(self, string:str, matches:REMatch, fragments:list[str]) -> str:
if matches:
start:int
end:int
i:int = len(fragments)
start, end = matches.span()
string = string[:start] + WMarkDown.item_mark[0] + str(len(fragments)) + WMarkDown.item_mark[1] + string[end:]
fragments += [matches.group(0)]
return string
@classmethod
def restore_marks(self, string:str, fragments:list[str]) -> str:
while True:
matches:REMatch = WMarkDown.item_mark[2].search(string)
if not matches:
break
i:int = int(matches.group(1))
_from:int
_to:int
_from, _to = matches.span()
string = string[:_from] + (fragments[i] if i >= 0 and i < len(fragments) else "") + string[_to:]
return string
@classmethod
def __doc_typed_format(self, typed:str) -> str:
return WMarkDown.filter_html_special_characters(typed.replace(' ', "").replace(',', ", "))
def module_code_doc(self, matches:REMatch, language:Optional[int] = HTML, path:Optional[str] = None) -> str|None:
if language & WMarkDown.HTML:
# print(matches.groups())
data:str = matches.group(1).strip()
base:REMatch = WMarkDown.re_code_doc.search(data)
return_type:str = WMarkDown.__doc_typed_format(base.group(2) or "void")
access:str = base.group(3) or "public"
name_space:str|None = base.group(5)
environment:str = base.group(6) or "global"
method:str = base.group(7)
parameters:str|None = base.group(9)
has_parameters:bool = parameters != None
default_value:str|None = base.group(11)
full_method:str = (name_space + "." if name_space else "") + method.strip()
arguments:str = ''
arguments_definition:str = ''
header:str = ('' +
'Name | ' +
'Required | ' +
'Nullable | ' +
'Typed | ' +
'Default Value | ' +
'
')
fragments:list[str] = []
matches:REMatch
arguments_l:int = 0
if access == "?":
access = "protected"
elif access == "!":
access = "private"
if environment == ":":
environment = "static"
elif environment == ".":
environment = "object"
if parameters:
while True:
matches = WMarkDown.re_code_doc_subarguments.search(parameters)
if matches:
parameters = WMarkDown.mark_replace(parameters, matches, fragments)
else:
break
else:
parameters = ""
for parameter in parameters.split(","):
matches = WMarkDown.re_code_doc_arguments.search(parameter.strip())
if matches:
scopes:str = matches.group(1) or ""
required:bool = "!" in scopes
nullish:bool = "?" in scopes
typed:str = WMarkDown.__doc_typed_format(WMarkDown.restore_marks(matches.group(2), fragments))
name:str = matches.group(3).strip()
default_value:str = WMarkDown.filter_html_special_characters(WMarkDown.restore_marks((matches.group(5) or "").strip(), fragments))
arguments += ('' +
('Required' if required else '') +
('Nullish' if nullish else '') +
'' + typed + '' +
'' + name + '' +
('' + default_value + '' if default_value else '') +
'')
arguments_definition += ('' +
'' + name + ' | ' +
'' + ('True' if required else 'False') + ' | ' +
'' + ('True' if nullish else 'False') + ' | ' +
'' + typed + ' | ' +
'' + default_value + ' | ' +
'
')
arguments_l += 1
return ('')
return None
def module_color_sample(self, matches:REMatch, language:Optional[int] = HTML, path:Optional[str] = None) -> str|None:
if language & WMarkDown.HTML:
color:str = matches.group(2) or matches.group(3)
return ('' +
'' +
'' + color + '' +
'')
return None
def module_presentation_links(self, matches:REMatch, language:Optional[int] = HTML, path:Optional[str] = None) -> str|None:
if language & WMarkDown.HTML:
items:list = []
i:int = -1
title:str
avatars:list[str]
links:list[str]
html_avatars:str = ''
html_list:str = ''
for line in WMarkDown.re_new_lines.split(matches.group(1)):
has_spaces:bool = WMarkDown.re_start_with_white_spaces.search(line) != None
data:str = line.strip()
if not data:
continue
if has_spaces:
if i == -1:
continue
if data[0] == "*":
items[i][1] += [data[1:]]
if ".k3y.pw/" in data:
items[i][1] += [data[1:].replace(".k3y.pw/", ".local/")]
else:
items[i][2] += [data]
else:
i += 1
items += [[line.strip(), [], []]]
for i, (title, avatars, links) in enumerate(items):
link:str
avatar:str
html_avatars += ('' +
('' +
WMarkDown.build_html_image(avatars) +
'' if len(links) else WMarkDown.build_html_image(avatars)) +
''.join(['' for link in links]) +
'')
html_list += ('' +
'' + title + '' +
'' +
'')
return ('')
return None
@classmethod
def remove_default_tabulations(self, data:str) -> str:
line:str
lines:list[str] = WMarkDown.re_new_lines.split(data)
spaces:int = len(data)
def callback(matches:REMatch):
return matches.group(0)[spaces:]
for line in lines:
if not line:
continue
white_spaces:int = len(WMarkDown.re_started_white_spaces.search(line).group(0))
if white_spaces < spaces:
spaces = white_spaces
return WMarkDown.re_lines.sub(callback, data)
def __build(self, data:str, language:int, path:str|None) -> str:
return ('' +
self.analyse(WMarkDown.remove_default_tabulations(data), language, WMarkDown.RAW, path) +
'
' +
'
')
def process(self, data:str, language:Optional[int] = HTML, path:Optional[str] = None) -> str:
results:str = ""
variables:dict[str, str] = {}
options:REMatch = WMarkDown.re_options.search(data)
if options:
_from:int
_to:int
options_data:str = options.group(3)
_from, _to = options.span()
while True:
option:REMatch = WMarkDown.re_option.search(options_data)
if not option:
break
variables[option.group(1).strip()] = option.group(2).strip()
options_data = options_data[option.span()[1]:]
data = data[:_from] + data[_to:]
while True:
matches:REMatch = WMarkDown.re_block_mark.search(data)
if not matches:
results += data
break
open_from:int
open_to:int
mark:str = matches.group(1)
re_close:REPattern = RECompile(r'^\s*' + mark, RE_MULTILINE) if mark else WMarkDown.re_block_html
open_from, open_to = matches.span()
results += data[:open_from]
data = data[open_to:]
close_matches:REMatch = re_close.search(data)
if close_matches:
close_from:int
close_to:int
close_from, close_to = close_matches.span()
results += self.__build(data[:close_from], language, path)
data = data[close_to:]
else:
results += self.__build(data, language, path)
break
return results, variables
def analyse(self, data:str, language:Optional[int] = HTML, mode:Optional[int] = RAW, path:Optional[str] = None) -> str:
response:str = ""
item_mode:int
items:dict[str, list[bool, int, int, REMatch]] = {key : [True, -1, -1, None] for key, (item_mode, pattern) in self.modules.items() if mode == WMarkDown.RAW or mode | item_mode == item_mode}
# j:int = 0
# m:int = 10
while len(items):
key:str
exists:bool
_from:int
_to:int
matches:REMatch
l:int = len(data)
selected:str|None = None
for key, (exists, _from, _to, matches) in items.items():
if not exists:
continue
pattern:REPattern = self.modules[key][1]
if _from < 0:
items[key][3] = matches = pattern.search(data)
if matches:
_from, _to = items[key][1], items[key][2] = matches.span()
else:
items[key][0] = False
continue
if _from < l:
l = _from
selected = key
if selected == None:
response += data
break
_from, _to, matches = items[selected][1:]
response += data[:_from] + getattr(self, "module_" + selected)(matches, language, path)
data = data[_to:]
for key in items.keys():
if items[key][3]:
items[key][1] -= _to
items[key][2] -= _to
# j += 1
# if j >= m:
# break
return response
def to_html(self, data:str, path:Optional[str] = None) -> str:
return self.analyse(data, WMarkDown.HTML, WMarkDown.RAW, path)