let r = String.raw
let whitespace = r`\s\u00AD\u2060\u200A\u200B\u200C\u200D\u20e2`
let url = r`[-\w/%&=#+~@$*'!?,.;:]*`
let url_final = r`[-\w/%&=#+~@$*']`
let big_regex = RegExp([
r`\b(?https?://${url}${url_final}([(]${url}[)](${url}${url_final})?)?)`,
r`(?<=^|\s)[##](?(?!\uFE0F)[^${whitespace}]*[^\p{P}${whitespace}])`, // note: must filter out #123
r`(?<=^|\s|[(])@(?[-a-zA-Z0-9]+([.][-a-zA-Z0-9]+)+)\b`,
r`\[(?[a-z]+)(?:=(?[^\x5D]*))?\]`,
r`\[/(?[a-z]+)\]`,
].join("|"), 'gu')
const STYLE_SURROUND = {
__proto__: null,
italic: Object.freeze(["/","/"]),
bold: Object.freeze(["**","**"]),
underline: Object.freeze(["__","__"]),
strikethrough: Object.freeze(["~~","~~"]),
code: Object.freeze(["`","`"]),
}
const TAG_FEATURES = {
__proto__: null,
i: Object.freeze({$type:MARKUP_TYPE, style:'italic', strip:STYLE_SURROUND['italic']}),
b: Object.freeze({$type:MARKUP_TYPE, style:'bold', strip:STYLE_SURROUND['bold']}),
u: Object.freeze({$type:MARKUP_TYPE, style:'underline', strip:STYLE_SURROUND['underline']}),
s: Object.freeze({$type:MARKUP_TYPE, style:'strikethrough', strip:STYLE_SURROUND['strikethrough']}),
code: Object.freeze({$type:MARKUP_TYPE, style:'code', strip:STYLE_SURROUND['code']}),
}
class ParseExample extends Parser {
constructor(text) {
super(text)
let open_tags = new Map() // type → feature
for (let match of this.run()) {
let start = match.index
let end = start + match[0].length
let g = match.groups
if (g.mention!=null) {
let feature = {$type:"app.bsky.richtext.facet#mention", did:g.mention}
this.overlay(start)
this.event_open(feature)
this.overlay(end, feature)
this.event_text(match[0])
this.event_close(feature)
} else if (g.link!=null) {
let feature = {$type:"app.bsky.richtext.facet#link", uri:g.link}
this.overlay(start)
this.event_open(feature)
this.overlay(end, feature)
this.event_text(g.link)
this.event_close(feature)
} else if (g.hashtag!=null && !/^\d+$/.test(g.hashtag)) {
let feature = {$type:"app.bsky.richtext.facet#tag", tag:g.hashtag}
this.overlay(start)
this.event_open(feature)
this.overlay(end, feature)
this.event_text(match[0])
this.event_close(feature)
} else if (g.close!=null && open_brackets.length) {
let feature = open_brackets.pop()
if (feature) {
this.overlay(start)
if (feature instanceof Array) {
for (let f of feature)
this.event_close(f) // nn
this.overlay(end, {$type:"#multi"})
} else {
this.event_close(feature, match[0])
this.overlay(end, feature)
}
} else {
this.event_text(match[0])
}
} else if (g.tag_open!=null) {
let feature = TAG_FEATURES[g.tag_open]
if (feature) {
feature = {...feature}
this.overlay(start)
this.overlay(end, feature)
this.event_open(feature, match[0])
open_tags.set(g.tag_open, feature)
} else {
this.event_text(match[0])
}
} else if (g.tag_close!=null) {
let feature = open_tags.get(g.tag_close)
if (feature) {
this.overlay(start)
this.overlay(end, feature)
this.event_close(feature, match[0])
} else {
this.event_text(match[0])
}
} else {
this.overlay(end)
this.event_text(match[0])
}
}
}
}
ParseExample.prototype.regex = big_regex